ActivityResultLauncher: qué es y cómo usarlo

Autor: IT Sectr Publicado: 2026-06-10 Tiempo de lectura: 9 min

ActivityResultLauncher es un componente de la API Activity Result de Android, presentado en la versión Activity 1.2.0 de la biblioteca androidx.activity. Reemplaza los métodos obsoletos startActivityForResult y onActivityResult, que formaban parte del SDK de Android desde su creación. Según Android Developers (2024), la nueva API elimina los problemas de acoplamiento fuerte con Activity y la falta de seguridad de tipos. ActivityResultLauncher se registra de antemano y utiliza un Contract para la tipificación estricta de los datos de entrada y salida.

Puntos clave

  • ActivityResultLauncher — una nueva API para obtener resultados de una Activity en lugar del obsoleto startActivityForResult
  • Contract — un objeto que define el tipo de datos de entrada y salida para un escenario específico
  • Registro se realiza mediante registerForActivityResult antes de llamar a launch
  • Callback se invoca después de que la Activity de destino finaliza con un resultado
  • API disponible para Activity, Fragment y Compose desde Activity 1.2.0

Qué es ActivityResultLauncher

ActivityResultLauncher es una clase del paquete androidx.activity.result que proporciona un mecanismo de seguridad de tipos para iniciar una Activity y recibir un resultado. El launcher se crea mediante el método registerForActivityResult, que acepta dos parámetros: Contract (describe los tipos de entrada y salida) y ActivityResultCallback (manejador del resultado). Después del registro, el launcher está listo para ser invocado mediante el método launch.

La diferencia clave con la API antigua es la separación del registro y el lanzamiento. El registro se realiza en la etapa de inicialización (Activity.onCreate o Fragment.onCreate), el callback se vincula al launcher una vez y se garantiza que se ejecute cuando regrese el resultado. Esto elimina el problema de que onActivityResult se ejecutara en un orden inesperado o en una Activity destruida.

ActivityResultLauncher admite todos los escenarios que antes se manejaban mediante onActivityResult: iniciar la cámara, la galería, solicitar contactos, permisos y Activities personalizadas. Además, la API es extensible: los desarrolladores pueden crear Contracts personalizados para escenarios específicos de intercambio de datos entre Activities.

Por qué Activity Result API reemplazó startActivityForResult

startActivityForResult ha sido parte del SDK de Android desde API Level 1 (2008) y siguió siendo la forma principal de obtener un resultado de una Activity durante más de 12 años. Sin embargo, este método tenía deficiencias fundamentales que Google solucionó en Activity Result API. Veamos los principales problemas y cómo los resuelve la nueva API.

Problema 1: Acoplamiento fuerte con Activity

El método startActivityForResult está vinculado a Activity y Fragment mediante requestCode — un número entero arbitrario pasado a onActivityResult. El desarrollador emparejaba manualmente el código con la operación iniciada, lo que provocaba errores en la reutilización de código y la herencia. ActivityResultLauncher elimina por completo requestCode: el callback se vincula a un launcher específico en el momento del registro y solo se invoca para él.

Problema 2: Pérdida del resultado al rotar la pantalla

Durante los cambios de configuración (rotación de pantalla, cambio de idioma), la Activity se recreaba y onActivityResult podía no ejecutarse — el callback se perdía. Activity Result API guarda y restaura automáticamente el estado del launcher mediante SavedStateRegistry, lo que garantiza que se reciba el resultado incluso después de recrear la Activity.

Problema 3: Falta de seguridad de tipos

La API antigua pasaba el resultado mediante Intent con un Bundle, donde las claves y los tipos de datos no eran verificados por el compilador. Activity Result API utiliza Contract — una interfaz genérica que define el tipo de datos de entrada (I) y el tipo de resultado (O). Los errores de discrepancia de tipos se detectan en tiempo de compilación, no en tiempo de ejecución.

CaracterísticastartActivityForResultActivityResultLauncher
RequestCodeGestión manual requeridaAutomático, no requerido
Seguridad de tiposNoContract genérico
Guardado al rotarSe pierdeSavedStateRegistry
API mínimaAPI Level 1Activity 1.2.0
Uso en ComposeNo compatiblerememberLauncherForActivityResult

Principales contratos de Activity Result API

Contract es la interfaz ActivityResultContract<I, O>, que define cómo iniciar una Activity y cómo interpretar el resultado. Google proporciona un conjunto de contratos integrados para escenarios típicos que cubren la mayoría de las necesidades del desarrollador.

StartIntentSenderForResult

StartIntentSenderForResult — un contrato básico para iniciar IntentSender. Se utiliza en escenarios del sistema, por ejemplo al autorizar mediante Google Sign-In o realizar pagos con Google Pay. El parámetro de entrada es PendingIntent, la salida es ActivityResult con código e Intent.

RequestMultiplePermissions

RequestMultiplePermissions — un contrato para solicitar múltiples permisos simultáneamente en Android 6.0+. El parámetro de entrada es un array de String con nombres de permisos, la salida es Map<String, Boolean> con el resultado de cada solicitud. Anteriormente esto requería análisis manual en onRequestPermissionsResult con coincidencia de códigos de solicitud.

TakePicture y TakeVideo

TakePicture — un contrato para tomar una foto mediante la cámara del sistema. La entrada es un Uri donde guardar la imagen, la salida es Boolean (éxito). TakeVideo funciona de manera similar con video. Estos contratos reemplazan el obsoleto MediaStore.ACTION_IMAGE_CAPTURE con comportamiento inestable en diferentes dispositivos.

GetContent y OpenDocument

GetContent — un contrato para seleccionar contenido mediante el selector del sistema. La entrada es un tipo MIME (por ejemplo, image/*), la salida es un Uri del archivo seleccionado. OpenDocument se diferencia por admitir selección múltiple y filtrado por tipos de documento. Ambos contratos funcionan a través de SAF (Storage Access Framework).

CreateDocument y OpenDocumentTree

CreateDocument — un contrato para crear un nuevo documento mediante el diálogo del sistema. El usuario elige un nombre y una carpeta, el sistema devuelve un Uri para escribir. OpenDocumentTree proporciona acceso a un directorio completo — el usuario selecciona una carpeta y la aplicación recibe un tree-uri para leer y escribir todos los archivos dentro.

Uso en Activity y Fragment

El patrón básico para usar ActivityResultLauncher en Android clásico consta de dos pasos: registro mediante registerForActivityResult en la inicialización y llamada a launch en respuesta a una acción del usuario. Veamos un ejemplo típico de selección de una imagen de la galería.

Registro y lanzamiento en Activity

Registra el launcher en onCreate de la Activity — esto garantiza que el callback esté listo antes de cualquier posible llamada. Nunca registres un launcher justo antes de lanzarlo — esto viola el contrato de la API y puede provocar la pérdida del resultado al recrear la Activity.

kotlin
class MainActivity : AppCompatActivity() {
    private val pickImageLauncher =
        registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
            uri?.let { binding.imageView.setImageURI(it) }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pickImageLauncher.launch("image/*")
    }
}

Uso en Fragment

En un Fragment, el registro se realiza en onCreate, onAttach o en la inicialización en onCreateView. FragmentActivity pasa el launcher a través de la Activity padre, por lo que el resultado se procesa dentro del Fragment, no en la Activity. Esto mejora la encapsulación en comparación con onActivityResult, donde todos los resultados de todos los Fragments se reunían en un solo método de Activity.

kotlin
class ProfileFragment : Fragment() {
    private val cameraLauncher =
        registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
            if (success) { updateProfilePhoto() }
        }

    fun takePhoto(photoUri: Uri) {
        cameraLauncher.launch(photoUri)
    }
}

ActivityResultLauncher en Jetpack Compose

Jetpack Compose proporciona una función composable especial para Activity Result API — rememberLauncherForActivityResult. A diferencia del enfoque clásico, en Compose el launcher se crea como un objeto vinculado al ciclo de vida del composable mediante remember. Esto permite usar Activity Result API completamente en un estilo declarativo sin acceso directo a Activity o Fragment.

rememberLauncherForActivityResult

rememberLauncherForActivityResult acepta un Contract y un callback, devolviendo un ActivityResultLauncher. El launcher se conserva durante la recomposición y se limpia automáticamente al salir de la composición. La llamada a launch ocurre en respuesta a un evento — por ejemplo, un clic en un botón o un cambio de estado.

kotlin
@Composable
fun PhotoPicker() {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        ActivityResultContracts.GetContent()
    ) { uri -> handleImage(uri) }

    Button(onClick = { launcher.launch("image/*") }) {
        Text("Elegir foto")
    }
}

Gestión de permisos en Compose

Las solicitudes de permisos en Compose también se realizan mediante rememberLauncherForActivityResult con el contrato RequestPermission o RequestMultiplePermissions. Google recomienda usar accompanist-permissions, pero internamente también utiliza Activity Result API. Para controlar el estado de los permisos, es conveniente almacenar el estado en remember o ViewModel.

Errores comunes y buenas prácticas

Activity Result API eliminó muchos problemas del enfoque anterior, pero un uso incorrecto puede provocar nuevos tipos de errores. Veamos los problemas más comunes y cómo evitarlos.

Error: registro dentro de una lambda o corrutina

El registro del launcher debe realizarse durante la inicialización del componente — en onCreate de Activity o en el inicializador de Fragment. Si registras el launcher dentro de una lambda, callback o corrutina, al recrear la Activity el registro puede realizarse de nuevo y el launcher anterior perderá la conexión con el resultado.

Error: registrar varios launchers con la misma clave

Cada launcher recibe una clave única para guardar el estado. Si registras dos launchers con el mismo Contract en un componente, SavedStateRegistry puede sobrescribir el estado de uno con el del otro. Android Studio advierte sobre esto mediante la regla lint UnnecessaryRegisterForActivityResult, pero es mejor controlar la unicidad manualmente.

Buena práctica: manejar siempre el resultado nulo

El usuario puede cancelar la acción — presionar el botón de retroceso del sistema, minimizar la aplicación o cambiar a otra aplicación. En este caso, el callback recibirá null o ActivityResult con RESULT_CANCELED. Siempre verifica si el resultado es null antes de usarlo para evitar NullPointerException.

Buena práctica: Contracts personalizados para lógica reutilizable

Si tu aplicación inicia con frecuencia escenarios similares — por ejemplo, seleccionar un contacto y devolver un nombre y teléfono — crea un Contract personalizado. Esto mejora la legibilidad del código y permite cambios centralizados en la lógica de inicio y manejo de resultados.

kotlin
class PickContactContract : ActivityResultContract<Void, ContactData?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE)

    override fun parseResult(resultCode: Int, intent: Intent?) =
        intent?.data?.let { queryContact(it) }
}

Preguntas frecuentes

¿Se puede usar ActivityResultLauncher en ViewModel?

No — ActivityResultLauncher requiere un contexto de Activity o Fragment para registrarse. Usa ViewModel solo para almacenar el estado, y crea el launcher en Activity o Fragment y pasa el resultado a ViewModel.

¿Qué SDK mínimo se requiere para Activity Result API?

Activity Result API está disponible desde la biblioteca activity-ktx 1.2.0. El SDK mínimo es API Level 14 (Android 4.0), pero la mayoría de los contratos solo funcionan en API Level 19+.

¿Qué sucede si se llama a launch dos veces antes de recibir un resultado?

Una llamada repetida a launch antes de que finalice la primera operación será ignorada. Activity Result API no admite lanzamientos paralelos — espera el callback de la primera operación antes de realizar una nueva llamada.

¿Cómo reemplazar onActivityResult en código antiguo?

La migración se realiza reemplazando la llamada a startActivityForResult por registerForActivityResult con el Contract correspondiente. Elimina onActivityResult y maneja el resultado en el callback del launcher. Google proporciona una guía de migración en la documentación de Android Developers.

¿ActivityResultLauncher funciona con bibliotecas como ML Kit o Barcode Scanner?

, muchas bibliotecas admiten la integración mediante ActivityResultContracts. Por ejemplo, ML Kit Barcode Scanner utiliza StartIntentSenderForResult para iniciar el escáner. Consulta la documentación de la biblioteca específica.

Resumen

  • ActivityResultLauncher — una API moderna con seguridad de tipos que reemplaza el obsoleto startActivityForResult
  • Contract define los tipos de datos de entrada y salida, eliminando la coincidencia manual de requestCode
  • Registro se realiza en la inicialización, el resultado se entrega garantizadamente mediante callback
  • Contratos integrados cubren cámara, galería, permisos, documentos y contactos
  • Jetpack Compose utiliza rememberLauncherForActivityResult para trabajar con la API
  • Contracts personalizados permiten reutilizar la lógica de lanzamiento entre componentes
  • La API conserva el estado durante cambios de configuración mediante SavedStateRegistry

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también