Muy buenas, me llamo Luis y para hoy les traigo un nuevo post.
En este momento, probablemente todos hayan oído hablar de la nueva API para obtener el resultado de una actividad. En pocas palabras, se ven así:
val getContent = registerForActivityResult(GetContent()) { // Handle the returned Uri } override fun onCreate(savedInstanceState: Bundle?) { // ... selectButton.setOnClickListener { getContent.launch("image/*") } }
Pasan de engancharse a onActivityResult
a un estilo de devolución de llamada más elegante. Y como siempre, viene con algunos problemas como se mencionó. aquí.
Si usa el componente de navegación y desea obtener un resultado de otro Fragment
, entonces buena suerte, porque tienes que hacer cosas locas como:
findNavController().currentBackStackEntry ?. savedStateHandle?.set("a", "b") findNavController().previousBackStackEntry ?. savedStateHandle ?. getLiveData<String>("a") ?. observe(viewLifecycleOwner) { // handle the result }
Y con algo de trampa como de costumbre.
En este punto, un pensamiento viene a mi mente: “Diablos, por qué tan complicado” y decido resolver este problema a mi manera. La forma en que es simple, fácil de usar y no se desaprovecha en unos pocos años.
Después de un experimento, se me ocurrió algo llamado ResultManager
@Singleton class ResultManager @Inject constructor() { private val requests = HashMap<String, CancellableContinuation<*>>() suspend fun <T> getResult(requestCode: String): T = suspendCancellableCoroutine { continuation -> requests[requestCode] = continuation continuation.invokeOnCancellation { requests.remove(requestCode) } } fun <T> sendResult(requestCode: String, result: T) { getRequest<T>(requestCode)?.resume(result) requests.remove(requestCode) } fun cancel(requestCode: String) { val request = getRequest<Any>(requestCode) request?.cancel() requests.remove(requestCode) } private fun <T> getRequest(requestCode: String): CancellableContinuation<T>? { val request = requests[requestCode] ?: return null @Suppress("UNCHECKED_CAST") return request as? CancellableContinuation<T> } }
La idea básica es cada vez que desee obtener un resultado de Actividad / Fragmento. Primero, genere un código de solicitud (un UUID servirá), llame a getResult inmediatamente (lo mejor es poner la llamada dentro de ViewModel). Luego, inicie la Actividad / Fragmento con el código de solicitud como parámetro.
// in ViewModel viewModelScope.launch(Dispatchers.IO) { val result = resultManager.getResult<FormatOption>(requestCode) // process result } // in Activity/Fragment/View val args = SelectFormatFragmentArgs(UUID.randomUUID().toString()) findNavController().navigate(R.id.select_format, args.toBundle())
Cuando quieras devolver el resultado, solo llama sendResult
// in where ever you want resultManager.sendResult(args.requestCode, result) controller.popBackStack() // in case you don't want to send back the result // cancel the request for result override fun onCleared() { super.onCleared() resultManager.cancel(requestCode = args.requestCode) }
No es necesario cancelar la llamada, todo seguirá funcionando debido a la llamada resultManager.getResult
ser suspendido hasta que alguien devuelva el resultado hasta entonces no pasará nada, dentro onClear
los viewModelScope
se canceló para que la corrutina de solicitud también se cancelara.
Y así, tiene una solución casera simple para lidiar con el problema del «resultado». Lo mejor de este enfoque:
- El ResultManager es neutral para la plataforma, por lo que puede hacer cosas flexibles como el resultado de una solicitud de actividad de un fragmento, una vista o literalmente cualquier cosa. También puede solicitar un resultado, luego enviar el resultado de 2 a 3 direcciones en la pila de navegación, siempre que tenga el código de solicitud, está listo para comenzar.
- El tipo de resultado puede ser cualquier cosa, no limitado a
Serialiable
oParcelable
Gracias por leer hasta el final.
Añadir comentario