Android CameraX en 2026: Captura de Fotos y Videos con Preguntas de Entrevista
Guía completa de CameraX 1.6 para Android: arquitectura lifecycle-aware, captura de fotos, grabación de video, integración con Jetpack Compose y preguntas de entrevista técnica.

CameraX simplifica el desarrollo de cámaras en Android al abstraer la complejidad del hardware detrás de una API lifecycle-aware. Con la versión 1.6.2 ahora estable y la migración a CameraPipe completada, CameraX ofrece el mismo stack de cámara de alto rendimiento que impulsa la aplicación de cámara Pixel.
CameraX 1.6 migró a CameraPipe, usa Media3 Muxer por defecto para VideoCapture y estabilizó la API SessionConfig. Los builders DSL de Kotlin (preview { }, imageCapture { }, videoCapture { }) reducen el boilerplate en un 40%.
Arquitectura de CameraX y Modelo de Casos de Uso
CameraX proporciona cuatro casos de uso principales que pueden ejecutarse simultáneamente: Preview, ImageCapture, ImageAnalysis y VideoCapture. Cada caso de uso se vincula a un LifecycleOwner, por lo que la cámara se inicia y detiene automáticamente con la actividad o fragmento.
El CameraProvider actúa como una factory que instancia estos casos de uso y los vincula a la cámara. Este desacoplamiento significa que el mismo código funciona en dispositivos de diferentes fabricantes sin preocuparse por las particularidades específicas de cada OEM.
class CameraSetup(
private val context: Context,
private val lifecycleOwner: LifecycleOwner
) {
private lateinit var cameraProvider: ProcessCameraProvider
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture<Recorder>? = null
suspend fun initialize() {
// ProcessCameraProvider vincula casos de uso al lifecycle
cameraProvider = ProcessCameraProvider.getInstance(context).await()
}
fun bindUseCases(previewView: PreviewView) {
// Desvincular casos de uso existentes antes de revincular
cameraProvider.unbindAll()
// Caso de uso Preview muestra el feed de la cámara
val preview = Preview.Builder()
.build()
.apply { setSurfaceProvider(previewView.surfaceProvider) }
// ImageCapture para fotos
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// VideoCapture con Recorder
val recorder = Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.FHD))
.build()
videoCapture = VideoCapture.withOutput(recorder)
// Vincular todos los casos de uso al lifecycle
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture,
videoCapture
)
}
}La llamada bindToLifecycle conecta los casos de uso tanto al hardware de la cámara como al lifecycle. Cuando el LifecycleOwner entra en estado STARTED, la cámara se abre. Cuando entra en estado STOPPED, la cámara se cierra automáticamente.
Implementando Captura de Fotos con ImageCapture
ImageCapture proporciona dos modos de captura: CAPTURE_MODE_MINIMIZE_LATENCY para una respuesta rápida del obturador, y CAPTURE_MODE_MAXIMIZE_QUALITY para tomas que requieren procesamiento intensivo. La elección depende de si la aplicación prioriza velocidad (apps sociales) o calidad (escáneres de documentos).
class PhotoCaptureManager(
private val context: Context,
private val imageCapture: ImageCapture
) {
private val mainExecutor = ContextCompat.getMainExecutor(context)
fun capturePhoto(onResult: (Uri?) -> Unit) {
// Generar nombre de archivo único con timestamp
val filename = "IMG_${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
// Almacenar en Pictures/CameraX en Android 10+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX")
}
}
val outputOptions = ImageCapture.OutputFileOptions.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
imageCapture.takePicture(
outputOptions,
mainExecutor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(results: ImageCapture.OutputFileResults) {
// results.savedUri contiene la URI de MediaStore
onResult(results.savedUri)
}
override fun onError(exception: ImageCaptureException) {
Log.e("PhotoCapture", "Captura fallida: ${exception.message}")
onResult(null)
}
}
)
}
}CameraX 1.5 agregó soporte para captura DNG (RAW) para aplicaciones que necesitan datos sin procesar del sensor. Se debe verificar la capacidad del dispositivo con ImageCapture.getImageCaptureCapabilities() antes de habilitar la salida RAW.
Grabación de Video con VideoCapture y Recorder
VideoCapture usa un objeto Recorder para manejar la codificación y el muxing. La API Recorder sigue un patrón prepare-start-stop que maneja automáticamente la sincronización de audio.
class VideoRecordingManager(
private val context: Context,
private val videoCapture: VideoCapture<Recorder>
) {
private var activeRecording: Recording? = null
fun startRecording(onEvent: (VideoRecordEvent) -> Unit): Boolean {
// Verificar si ya está grabando
if (activeRecording != null) return false
val filename = "VID_${System.currentTimeMillis()}.mp4"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX")
}
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(
context.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)
.setContentValues(contentValues)
.build()
// Preparar grabación con audio si se tiene permiso
val pendingRecording = videoCapture.output
.prepareRecording(context, mediaStoreOutput)
if (hasAudioPermission()) {
pendingRecording.withAudioEnabled()
}
// Iniciar grabación y almacenar referencia
activeRecording = pendingRecording.start(
ContextCompat.getMainExecutor(context)
) { event ->
onEvent(event)
// Limpiar referencia cuando termina la grabación
if (event is VideoRecordEvent.Finalize) {
activeRecording = null
}
}
return true
}
fun stopRecording() {
activeRecording?.stop()
}
fun pauseRecording() {
activeRecording?.pause()
}
fun resumeRecording() {
activeRecording?.resume()
}
private fun hasAudioPermission(): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
}
}CameraX 1.5 agregó soporte para video en cámara lenta. Las capacidades pueden consultarse con Recorder.getHighSpeedVideoCapabilities() antes de habilitar grabación a 120fps o 240fps.
¿Listo para aprobar tus entrevistas de Android?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
CameraX con Jetpack Compose
CameraX 1.5 introdujo el artifact camera-compose con un composable CameraXViewfinder. Para aplicaciones que aún usan PreviewView basado en View, AndroidView proporciona interoperabilidad.
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
onPreviewViewCreated: (PreviewView) -> Unit
) {
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
PreviewView(context).apply {
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
scaleType = PreviewView.ScaleType.FILL_CENTER
onPreviewViewCreated(this)
}
}
)
}
@Composable
fun CameraScreen(viewModel: CameraViewModel = viewModel()) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
viewModel.initializeCamera(context, lifecycleOwner)
}
Box(modifier = Modifier.fillMaxSize()) {
CameraPreview(
onPreviewViewCreated = { previewView ->
viewModel.bindPreview(previewView)
}
)
// Botones de captura en overlay
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(32.dp),
horizontalArrangement = Arrangement.spacedBy(24.dp)
) {
IconButton(
onClick = { viewModel.capturePhoto() }
) {
Icon(Icons.Default.Camera, "Tomar foto")
}
IconButton(
onClick = { viewModel.toggleRecording() }
) {
Icon(
if (viewModel.isRecording) Icons.Default.Stop
else Icons.Default.Videocam,
"Grabar video"
)
}
}
}
}Para implementaciones Compose-first, la guía de preguntas de entrevista de Jetpack Compose cubre patrones de integración con ViewModel que se aplican directamente a la gestión del estado de la cámara.
Manejo de Permisos de Cámara
Android requiere el permiso CAMERA en tiempo de ejecución. RECORD_AUDIO solo es necesario cuando se graba video con sonido. El flujo de permisos debe explicar por qué se necesita el acceso antes de solicitarlo.
class CameraPermissionHandler(private val activity: ComponentActivity) {
private val requiredPermissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
private val permissionLauncher = activity.registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] == true
val audioGranted = permissions[Manifest.permission.RECORD_AUDIO] == true
if (cameraGranted) {
onCameraPermissionGranted(audioGranted)
} else {
onCameraPermissionDenied()
}
}
fun checkAndRequestPermissions() {
when {
hasAllPermissions() -> onCameraPermissionGranted(hasAudioPermission())
shouldShowRationale() -> showPermissionRationale()
else -> permissionLauncher.launch(requiredPermissions)
}
}
private fun hasAllPermissions(): Boolean {
return requiredPermissions.all { permission ->
ContextCompat.checkSelfPermission(
activity, permission
) == PackageManager.PERMISSION_GRANTED
}
}
private fun hasAudioPermission(): Boolean {
return ContextCompat.checkSelfPermission(
activity,
Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
}
private fun shouldShowRationale(): Boolean {
return requiredPermissions.any { permission ->
ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
}
}
}Preguntas de Entrevista sobre CameraX
CameraX aparece en entrevistas de Android cuando se discute integración de cámara, gestión del lifecycle o abstracción de hardware. Estas preguntas evalúan la comprensión de la arquitectura y los detalles de implementación práctica.
¿Por qué usar CameraX en lugar de Camera2?
CameraX abstrae las particularidades específicas de dispositivos que Camera2 expone directamente. Una implementación de Camera2 requiere manejar cientos de soluciones alternativas específicas para cada dispositivo. CameraX incorpora estas correcciones en la biblioteca, probadas contra el CameraX Test Lab que cubre más de 150 modelos de dispositivos.
La vinculación lifecycle-aware elimina la gestión manual de recursos. Camera2 requiere llamadas explícitas de open/close que causan fugas de memoria si no se manejan correctamente en cada callback del lifecycle.
¿Cómo vincula CameraProvider los casos de uso al lifecycle?
CameraProvider usa el patrón LifecycleObserver internamente. Al llamar a bindToLifecycle(), registra un observador en el LifecycleOwner proporcionado. Este observador recibe eventos ON_START y ON_STOP para abrir y cerrar la cámara.
El punto clave: CameraX no inicia la cámara inmediatamente al vincular. Espera a que el lifecycle alcance el estado STARTED, lo cual coincide con el timing de Activity.onStart().
¿Qué sucede cuando se vinculan casos de uso incompatibles?
No todas las combinaciones de casos de uso funcionan en todos los dispositivos. Preview + VideoCapture + ImageAnalysis + ImageCapture puede fallar en hardware antiguo. CameraX lanza una IllegalArgumentException en el momento de vincular, no en tiempo de ejecución.
La solución es verificar CameraProvider.hasCamera() y las capacidades del dispositivo antes de vincular combinaciones complejas. Para aplicaciones críticas, probar explícitamente en dispositivos de gama baja.
Explicar la diferencia entre CAPTURE_MODE_MINIMIZE_LATENCY y CAPTURE_MODE_MAXIMIZE_QUALITY
MINIMIZE_LATENCY reduce el lag del obturador usando pipelines de procesamiento más rápidos, posiblemente omitiendo algunos post-procesamientos. MAXIMIZE_QUALITY aplica procesamiento completo de HDR+, reducción de ruido y captura multi-frame en dispositivos compatibles.
El compromiso: el modo latencia captura en 100-200ms, el modo calidad puede tomar 500ms-1s dependiendo de la iluminación. Las apps sociales típicamente eligen latencia. Las apps de escaneo de documentos eligen calidad.
¿Cómo maneja CameraX la rotación del dispositivo?
CameraX maneja automáticamente la rotación a través del ajuste de target rotation. Por defecto, usa la rotación del Display, pero las apps pueden sobrescribirlo con setTargetRotation(). Los datos EXIF de la imagen de salida contienen la etiqueta de orientación correcta.
Para PreviewView, el scaleType maneja las diferencias de relación de aspecto. FILL_CENTER recorta para llenar, FIT_CENTER muestra letterboxing. La elección depende de si la app prioriza la vista previa a pantalla completa o evitar el recorte.
Dependencias y Configuración para CameraX 1.6
La biblioteca CameraX está dividida en múltiples artifacts. Incluir solo lo que la app usa para minimizar el tamaño del APK.
dependencies {
val cameraxVersion = "1.6.2"
// Biblioteca core requerida para todos los casos de uso
implementation("androidx.camera:camera-core:$cameraxVersion")
// Implementación Camera2 (requerida)
implementation("androidx.camera:camera-camera2:$cameraxVersion")
// Integración con Lifecycle
implementation("androidx.camera:camera-lifecycle:$cameraxVersion")
// Caso de uso VideoCapture
implementation("androidx.camera:camera-video:$cameraxVersion")
// PreviewView y componentes de UI de cámara
implementation("androidx.camera:camera-view:$cameraxVersion")
// Opcional: Extensions (Modo nocturno, HDR, etc.)
implementation("androidx.camera:camera-extensions:$cameraxVersion")
// Opcional: Efectos en tiempo real
implementation("androidx.camera:camera-effects:$cameraxVersion")
}
android {
// CameraX requiere bytecode Java 11
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}El Manifest necesita declaraciones de características y permisos:
<!-- AndroidManifest.xml -->
<manifest>
<!-- Requisito de hardware de cámara -->
<uses-feature android:name="android.hardware.camera.any" />
<!-- Permisos en tiempo de ejecución -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>Los patrones relacionados para Kotlin Coroutines aplican al envolver llamadas ListenableFuture de CameraX en funciones suspend.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Qué Cambia CameraX 1.6 para Apps en Producción
CameraX 1.6 trajo tres cambios que afectan implementaciones existentes:
-
Backend CameraPipe: El stack de cámara interno ahora usa CameraPipe, la misma arquitectura que la cámara Pixel. Esto mejora el rendimiento en dispositivos Pixel y proporciona una base más limpia para características avanzadas. El código existente no necesita cambios.
-
Media3 Muxer por defecto: VideoCapture ahora usa Media3 Muxer en lugar de MediaMuxer. Esto mejora la resiliencia ante crashs: si la app termina durante la grabación, el archivo de video permanece reproducible. La superficie de la API no cambia.
-
API SessionConfig estable: Las apps ahora pueden configurar ajustes avanzados de cámara a través de SessionConfig sin anotaciones experimentales. Esto habilita características como el uso simultáneo de cámaras frontal/trasera en dispositivos compatibles.
Ruta de migración: Actualizar la versión de dependencia. Probar en dispositivos que anteriormente tenían problemas. Las notas de lanzamiento de CameraX documentan cualquier cambio de comportamiento entre versiones.
- CameraX usa vinculación lifecycle-aware:
bindToLifecycle()conecta casos de uso tanto al lifecycle de la cámara como de la Activity - Los modos de ImageCapture intercambian latencia por calidad: elegir basándose en el caso de uso, no en la capacidad del dispositivo
- VideoCapture requiere configuración del Recorder antes de vincular: preparar las opciones de salida antes de iniciar
- El manejo de permisos debe explicar el propósito antes de solicitar: reduce las tasas de rechazo
- CameraX 1.6 migró a CameraPipe internamente: no se requieren cambios de API, pero probar en dispositivos objetivo
- Las preguntas de entrevista se enfocan en gestión del lifecycle, combinaciones de casos de uso y comparación con Camera2
¿Sabrías detectar el bug en Android?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 11 de septiembre de 2026
Compartir
Artículos relacionados

Modularización Android en 2026: Arquitectura Multi-Módulo y Preguntas de Entrevista
Guía completa sobre modularización Android en 2026. Aprende a estructurar una arquitectura multi-módulo, usar catálogos de versiones Gradle y dominar las preguntas de entrevista técnica esenciales.

Jetpack Navigation Compose en 2026: Navegación Type-Safe y Preguntas de Entrevista
Domina Jetpack Navigation Compose con navegación type-safe en Kotlin. Guía completa con ejemplos de código, manejo de argumentos y preguntas frecuentes de entrevistas Android en 2026.

Android WorkManager en 2026: Tareas en Segundo Plano, Restricciones y Preguntas de Entrevista
Guía completa sobre WorkManager para Android en 2026: creación de Workers, restricciones de ejecución, encadenamiento de tareas y preguntas técnicas de entrevista.