¡Es hora de perder enActivityResult!

¿Por qué debería perder onActivityResult?

¿Cómo iniciar una nueva actividad y obtener el valor de retorno?
Su respuesta es definitivamente startActivityForResulty onActivityResult. Así es, en algunos escenarios, como iniciar la cámara del sistema para tomar fotos y volver a la página actual para obtener los datos de la foto, no tenemos otra opción que procesarla en onActivityResult.

En el último Activity 1.2.0-alpha02y el fragmento 1.3.0-alpha02en, Google ofrece una nueva API Actividad resultado, por lo que podemos ser más elegante onActivityResult proceso. Antes de presentar la nueva API, ¿podríamos pensar en por qué Google perdió en ActivityResult?
Reduce el código repetitivo, el desacoplamiento y es más fácil de probar.
Para el escenario más simple, MainActivityvaya a SecondActivity, el SecondActivitybotón central activa el retorno y devuelve el valor. El código en SecondActivity es simple:

class SecondActivity : AppCompatActivity(R.layout.activity_second){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        back.setOnClickListener {
            setResult(Activity.RESULT_OK, Intent().putExtra("value","I am back !"))
            finish()
        }
    }
}

Ahora es compatible para pasar el layoutId directamente en el constructor AppCompatActivity () sin la necesidad de setContentView () adicional.

Volver MainActivityen, según la redacción tradicional, es la siguiente:

class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private val REQUEST_CODE = 1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        jump.setOnClickListener { jump() }
    }

    private fun jump() {
        startActivityForResult(Intent(this, SecondActivity::class.java), REQUEST_CODE)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE) {
            toast(data?.getStringExtra("value") ?: "")
        }
    }API
}
  • Defina un REQUEST_CODE, cuando hay varios en la misma página, asegúrese de no repetir
  • Llame a startActivityForResult
  • Reciba la devolución de llamada en onActivityResult y juzgue requestCode, resultCode

No hay falta de código repetitivo repetitivo en la lógica anterior, y la mayoría de ellos están acoplados al controlador de vista (Actividad / Fragmento), lo que dificulta la prueba. Una mirada más cercana no es tan razonable.

Es posible que solo tengamos esta opción durante mucho tiempo, por lo que rara vez vemos a alguien quejándose de onActivityResult. Los ingenieros de Google que siguen mejorando mejoran este problema para nosotros.

Echemos un vistazo a cómo usar la última API de resultados de actividad.

API de resultados de actividad

    private val startActivity =
        prepareCall(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult? ->
            toast(result?.data?.getStringExtra("value") ?: "")
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        jump.setOnClickListener { jump() }
    }

    private fun jump() {
        startActivity.launch(Intent(this,SecondActivity::class.java))
    }

Bueno, es así de simple. Hay dos métodos principales, prepareCall()y launch(). Desmontar para analizar uno por uno.

    public <I, O> ActivityResultLauncher<I> prepareCall(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return prepareCall(contraguanxict, mActivityResultRegistry, callback);
    }

El método prepare () recibe dos parámetros ActivityResultContracty el ActivityResultCallbackvalor de retorno es ActivityResultLauncher. Estos pocos nombres son muy buenos, vea el nombre y la comprensión.

ActivityResultContract

ActivityResultContractPuede entenderse como un protocolo, es una clase abstracta que proporciona dos capacidades, createIntenty parseResult. Estas dos capacidades se entienden bien en la Actividad de inicio, createIntent es responsable de proporcionar Intent para startActivityForResult, y parseResult es responsable de procesar los resultados obtenidos en onActivityResult.

En el ejemplo anterior, la clase de implementación de protocolo aprobada en el método prepare () es StartActivityForResult. Es una ActivityResultContractsclase clase interna estática. Además de StartActivityForResultAdemás, el funcionario también ofrece por defecto RequestPermissions, Dial, RequestPermission, TakePicture, son ActivityResultContractla clase de implementación.

Por lo tanto, además de la simplificación startActivityForResult, las solicitudes de permisos, las llamadas telefónicas y las fotos se pueden simplificar a través de la API de resultados de actividad. Además de utilizar el valor predeterminado oficial, también podemos implementar ActivityResultContract nosotros mismos, lo que se demostrará en el código que se encuentra detrás.

ActivityResultCallback

public interface ActivityResultCallback<O> {

    /**
     * Called when result is available
     */
    void onActivityResult(@SuppressLint("UnknownNullness") O result);
}

Esto es relativamente simple. Cuando el resultado de devolución de llamada esté disponible, notifique a través de esta interfaz. Una cosa a tener en cuenta es que debido a la limitación genérica del método prepare (), el valor de retorno aquí debe ser de tipo seguro. La siguiente tabla muestra la correspondencia entre los protocolos integrados del sistema y sus tipos de valor de retorno.

Github

ActivityResultLauncher

prepare()El valor de retorno del método.

prepare()De hecho, el método llama al ActivityResultRegistry.registerActivityResultCallback()método, no se analiza aquí la fuente específica, la parte posterior escribirá una resolución de la fuente separada. El proceso general es generar automáticamente el código de solicitud, registrar la devolución de llamada y almacenarlo, vincular el ciclo de vida Lifecycle.Event.ON_DESTROYy desvincular automáticamente el registro cuando se recibe un evento.

        @Override
        public <I, O> void invoke(
                final int requestCode,
                @NonNull ActivityResultContract<I, O> contract,
                I input) {
            Intent intent = contract.createIntent(input);
            if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) {
           	// handle request permissions
            } else {
                ComponentActivity.this.startActivityForResult(intent, requestCode);
            }
        }

Corté la parte central que maneja los permisos de solicitud. Esto se ve claro. Originalmente preparé un análisis de código fuente separado, y el código fuente central estaba terminado.

Como se mostró anteriormente startActivityForResult(), muestremos nuevamente la solicitud de permiso.

    private val requestPermission = prepareCall(ActivityResultContracts.RequestPermission()){
        result -> toast("request permission $result")
    }

    requestPermission.launch(Manifest.permission.READ_PHONE_STATE)

Hacer una llamada telefónica y tomar una foto no se mostrará aquí. Todo el código de muestra se ha subido a mi Github.

¿Cómo personalizar el valor de retorno?

Los protocolos antes mencionados están predeterminados por el sistema, y ​​los valores de retorno también son fijos. Entonces, ¿cómo devolver el valor de un tipo personalizado? De hecho, muy simple, personalizar ActivityResultContractél.

Tomemos el TakePictureejemplo, el valor de retorno predeterminado es Bitmap, ahora lo dejamos regresar Drawable.

    private class TakePicDrawable : ActivityResultContract<Void,Drawable>(){

        override fun createIntent(input: Void?): Intent {
            return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        }

        override fun parseResult(resultCode: Int, intent: Intent?): Drawable? {
            if (resultCode != Activity.RESULT_OK || intent == null) return null
            val bitmap = intent.getParcelableExtra<Bitmap>("data")
            return BitmapDrawable(bitmap)
        }
    }

Uso:

private val takePictureCustom = prepareCall(TakePicDrawable()) { result ->
    toast("take picture : $result")
}

pictureCustomBt.setOnClickListener {  takePictureCustom()}

De esta manera, puede llamar a la cámara del sistema para tomar una fotografía y obtener el objeto Dibujable en la devolución de llamada resultante.

¿Qué pasa con el desacoplamiento?

A veces podemos realizar algunas operaciones de procesamiento complejas en la devolución de llamada de resultados, ya sea la onActivityResult()escritura anterior o la anterior, que se acoplan directamente en el controlador de vista. A través de la nueva API de resultados de actividad, también podemos manejar devoluciones de llamadas de resultados en clases separadas, logrando realmente una única responsabilidad.
De hecho Actividad Resultado operaciones centrales de la API son realizadas por ActivityResultRegistry, ComponentActivitycontiene un ActivityResultRegistrytema:

  @NonNull
    public ActivityResultRegistry getActivityResultRegistry() {
        return mActivityResultRegistry;
   }

Actividad a partir de ahora para completar la operación, es necesario proporcionar una entidad ajena a ActivityResultRegistryobjetos para el registro de los resultados de la devolución de llamada. Al mismo tiempo, por lo general, mediante la implementación de LifecycleObserveruna interfaz, una unión LifecycleOwnerde desagregación del registro automático. El código completo es el siguiente:

class TakePhotoObserver(
    private val registry: ActivityResultRegistry,
    private val func: (Bitmap) -> Unit
) : DefaultLifecycleObserver {

    private lateinit var takePhotoLauncher: ActivityResultLauncher<Void?>

    override fun onCreate(owner: LifecycleOwner) {
        takePhotoLauncher = registry.registerActivityResultCallback(
            "key",
            ActivityResultContracts.TakePicture()
        ) { bitmap ->
            func(bitmap)
        }
    }

    fun takePicture(){
        takePhotoLauncher()
    }
}

¿Jugar más flores?

Vi algunos escritos elegantes en Github y los comparto con ustedes.

class TakePhotoLiveData(private val registry: ActivityResultRegistry) : LiveData<Bitmap>() {

    private lateinit var takePhotoLauncher : ActivityResultLauncher<Intent>

    override fun onActive() {
        super.onActive()
        registry.registerActivityResultCallback("key",
        ActivityResultContracts.TakePicture()){
            result -> value = result 
        }
    }

    override fun onInactive() {
        super.onInactive()
        takePhotoLauncher.dispose()
    }

}

Regístrese y desvincule automáticamente vinculando LiveData.

Pasado

Todo el código de muestra se ha subido a mi Github en
github.com/lulululbj/A  ...

Comparta especialmente el análisis de las preguntas reales de la entrevista de byte beating, agregue VX: q1607947758 para obtenerlo gratis

  • Video de arquitectura avanzada de Android

imagen

Publicado 488 artículos originales · elogiado 85 · 230,000 vistas +

Supongo que te gusta

Origin blog.csdn.net/Coo123_/article/details/105203861
Recomendado
Clasificación