Android CameraX em 2026: Captura de Fotos e Vídeos com Perguntas de Entrevista

Guia completo do CameraX 1.6 para Android: arquitetura lifecycle-aware, captura de fotos, gravação de vídeo, integração com Jetpack Compose e perguntas de entrevista técnica.

Android CameraX em 2026: Captura de Fotos e Vídeos com Perguntas de Entrevista

O CameraX simplifica o desenvolvimento de câmeras no Android ao abstrair a complexidade do hardware por trás de uma API lifecycle-aware. Com a versão 1.6.2 agora estável e a migração para o CameraPipe concluída, o CameraX oferece a mesma stack de câmera de alto desempenho que alimenta o aplicativo de câmera do Pixel.

Mudanças Principais no CameraX 1.6

O CameraX 1.6 migrou para o CameraPipe, usa o Media3 Muxer por padrão para VideoCapture e estabilizou a API SessionConfig. Os builders DSL do Kotlin (preview { }, imageCapture { }, videoCapture { }) reduzem o boilerplate em 40%.

Arquitetura do CameraX e Modelo de Casos de Uso

O CameraX fornece quatro casos de uso principais que podem ser executados simultaneamente: Preview, ImageCapture, ImageAnalysis e VideoCapture. Cada caso de uso se vincula a um LifecycleOwner, de modo que a câmera inicia e para automaticamente com a activity ou fragment.

O CameraProvider atua como uma factory que instancia esses casos de uso e os vincula à câmera. Esse desacoplamento significa que o mesmo código funciona em dispositivos de diferentes fabricantes sem se preocupar com particularidades específicas de cada OEM.

CameraSetup.ktkotlin
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 ao lifecycle
        cameraProvider = ProcessCameraProvider.getInstance(context).await()
    }
    
    fun bindUseCases(previewView: PreviewView) {
        // Desvincular casos de uso existentes antes de revincular
        cameraProvider.unbindAll()
        
        // Caso de uso Preview exibe o feed da câmera
        val preview = Preview.Builder()
            .build()
            .apply { setSurfaceProvider(previewView.surfaceProvider) }
        
        // ImageCapture para fotos
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .build()
        
        // VideoCapture com Recorder
        val recorder = Recorder.Builder()
            .setQualitySelector(QualitySelector.from(Quality.FHD))
            .build()
        videoCapture = VideoCapture.withOutput(recorder)
        
        // Vincular todos os casos de uso ao lifecycle
        cameraProvider.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview,
            imageCapture,
            videoCapture
        )
    }
}

A chamada bindToLifecycle conecta os casos de uso tanto ao hardware da câmera quanto ao lifecycle. Quando o LifecycleOwner entra no estado STARTED, a câmera abre. Quando entra no estado STOPPED, a câmera fecha automaticamente.

Implementando Captura de Fotos com ImageCapture

O ImageCapture fornece dois modos de captura: CAPTURE_MODE_MINIMIZE_LATENCY para resposta rápida do obturador, e CAPTURE_MODE_MAXIMIZE_QUALITY para fotos que exigem processamento intensivo. A escolha depende de se o aplicativo prioriza velocidade (apps sociais) ou qualidade (scanners de documentos).

PhotoCaptureManager.ktkotlin
class PhotoCaptureManager(
    private val context: Context,
    private val imageCapture: ImageCapture
) {
    private val mainExecutor = ContextCompat.getMainExecutor(context)
    
    fun capturePhoto(onResult: (Uri?) -> Unit) {
        // Gerar nome de arquivo único com timestamp
        val filename = "IMG_${System.currentTimeMillis()}.jpg"
        
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            // Armazenar em Pictures/CameraX no 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 contém a URI do MediaStore
                    onResult(results.savedUri)
                }
                
                override fun onError(exception: ImageCaptureException) {
                    Log.e("PhotoCapture", "Captura falhou: ${exception.message}")
                    onResult(null)
                }
            }
        )
    }
}

O CameraX 1.5 adicionou suporte à captura DNG (RAW) para aplicativos que precisam de dados não processados do sensor. Deve-se verificar a capacidade do dispositivo com ImageCapture.getImageCaptureCapabilities() antes de habilitar a saída RAW.

Gravação de Vídeo com VideoCapture e Recorder

O VideoCapture usa um objeto Recorder para lidar com codificação e muxing. A API Recorder segue um padrão prepare-start-stop que gerencia automaticamente a sincronização de áudio.

VideoRecordingManager.ktkotlin
class VideoRecordingManager(
    private val context: Context,
    private val videoCapture: VideoCapture<Recorder>
) {
    private var activeRecording: Recording? = null
    
    fun startRecording(onEvent: (VideoRecordEvent) -> Unit): Boolean {
        // Verificar se já está gravando
        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 gravação com áudio se tiver permissão
        val pendingRecording = videoCapture.output
            .prepareRecording(context, mediaStoreOutput)
        
        if (hasAudioPermission()) {
            pendingRecording.withAudioEnabled()
        }
        
        // Iniciar gravação e armazenar referência
        activeRecording = pendingRecording.start(
            ContextCompat.getMainExecutor(context)
        ) { event ->
            onEvent(event)
            
            // Limpar referência quando a gravação terminar
            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
    }
}

O CameraX 1.5 adicionou suporte a vídeo em câmera lenta. As capacidades podem ser consultadas com Recorder.getHighSpeedVideoCapabilities() antes de habilitar gravação em 120fps ou 240fps.

Pronto para mandar bem nas entrevistas de Android?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

CameraX com Jetpack Compose

O CameraX 1.5 introduziu o artifact camera-compose com um composable CameraXViewfinder. Para aplicativos que ainda usam PreviewView baseado em View, o AndroidView fornece interoperabilidade.

CameraPreviewComposable.ktkotlin
@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)
            }
        )
        
        // Botões de captura em overlay
        Row(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .padding(32.dp),
            horizontalArrangement = Arrangement.spacedBy(24.dp)
        ) {
            IconButton(
                onClick = { viewModel.capturePhoto() }
            ) {
                Icon(Icons.Default.Camera, "Tirar foto")
            }
            
            IconButton(
                onClick = { viewModel.toggleRecording() }
            ) {
                Icon(
                    if (viewModel.isRecording) Icons.Default.Stop
                    else Icons.Default.Videocam,
                    "Gravar vídeo"
                )
            }
        }
    }
}

Para implementações Compose-first, o guia de perguntas de entrevista sobre Jetpack Compose cobre padrões de integração com ViewModel que se aplicam diretamente ao gerenciamento de estado da câmera.

Tratamento de Permissões da Câmera

O Android requer a permissão CAMERA em tempo de execução. RECORD_AUDIO só é necessária ao gravar vídeo com som. O fluxo de permissões deve explicar por que o acesso é necessário antes de solicitá-lo.

PermissionHandler.ktkotlin
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)
        }
    }
}

Perguntas de Entrevista sobre CameraX

O CameraX aparece em entrevistas de Android quando se discute integração de câmera, gerenciamento de lifecycle ou abstração de hardware. Essas perguntas testam a compreensão da arquitetura e detalhes práticos de implementação.

Por que usar CameraX em vez de Camera2?

O CameraX abstrai as particularidades específicas de dispositivos que o Camera2 expõe diretamente. Uma implementação de Camera2 requer lidar com centenas de soluções alternativas específicas para cada dispositivo. O CameraX incorpora essas correções na biblioteca, testadas contra o CameraX Test Lab que cobre mais de 150 modelos de dispositivos.

A vinculação lifecycle-aware elimina o gerenciamento manual de recursos. O Camera2 requer chamadas explícitas de open/close que causam vazamentos de memória se não forem tratadas corretamente em cada callback do lifecycle.

Como o CameraProvider vincula casos de uso ao lifecycle?

O CameraProvider usa o padrão LifecycleObserver internamente. Ao chamar bindToLifecycle(), ele registra um observador no LifecycleOwner fornecido. Esse observador recebe eventos ON_START e ON_STOP para abrir e fechar a câmera.

O ponto-chave: o CameraX não inicia a câmera imediatamente ao vincular. Ele aguarda o lifecycle atingir o estado STARTED, que corresponde ao timing de Activity.onStart().

O que acontece ao vincular casos de uso incompatíveis?

Nem todas as combinações de casos de uso funcionam em todos os dispositivos. Preview + VideoCapture + ImageAnalysis + ImageCapture pode falhar em hardware antigo. O CameraX lança uma IllegalArgumentException no momento da vinculação, não em tempo de execução.

A solução é verificar CameraProvider.hasCamera() e as capacidades do dispositivo antes de vincular combinações complexas. Para aplicativos críticos, testar explicitamente em dispositivos de entrada.

Explicar a diferença entre CAPTURE_MODE_MINIMIZE_LATENCY e CAPTURE_MODE_MAXIMIZE_QUALITY

MINIMIZE_LATENCY reduz o lag do obturador usando pipelines de processamento mais rápidos, possivelmente pulando alguns pós-processamentos. MAXIMIZE_QUALITY aplica processamento completo de HDR+, redução de ruído e captura multi-frame em dispositivos compatíveis.

O trade-off: o modo latência captura em 100-200ms, o modo qualidade pode levar 500ms-1s dependendo da iluminação. Apps sociais tipicamente escolhem latência. Apps de scanner de documentos escolhem qualidade.

Como o CameraX lida com a rotação do dispositivo?

O CameraX lida automaticamente com a rotação através da configuração de target rotation. Por padrão, ele usa a rotação do Display, mas os apps podem sobrescrever com setTargetRotation(). Os dados EXIF da imagem de saída contêm a tag de orientação correta.

Para PreviewView, o scaleType lida com diferenças de proporção. FILL_CENTER corta para preencher, FIT_CENTER mostra letterboxing. A escolha depende de se o app prioriza visualização em tela cheia ou evitar cortes.

Dependências e Configuração para CameraX 1.6

A biblioteca CameraX é dividida em múltiplos artifacts. Incluir apenas o que o app usa para minimizar o tamanho do APK.

build.gradle.kts (Module: app)kotlin
dependencies {
    val cameraxVersion = "1.6.2"
    
    // Biblioteca core necessária para todos os casos de uso
    implementation("androidx.camera:camera-core:$cameraxVersion")
    
    // Implementação Camera2 (necessária)
    implementation("androidx.camera:camera-camera2:$cameraxVersion")
    
    // Integração com Lifecycle
    implementation("androidx.camera:camera-lifecycle:$cameraxVersion")
    
    // Caso de uso VideoCapture
    implementation("androidx.camera:camera-video:$cameraxVersion")
    
    // PreviewView e componentes de UI da câmera
    implementation("androidx.camera:camera-view:$cameraxVersion")
    
    // Opcional: Extensions (Modo noturno, HDR, etc.)
    implementation("androidx.camera:camera-extensions:$cameraxVersion")
    
    // Opcional: Efeitos em tempo real
    implementation("androidx.camera:camera-effects:$cameraxVersion")
}

android {
    // CameraX requer bytecode Java 11
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

O Manifest precisa de declarações de recursos e permissões:

xml
<!-- AndroidManifest.xml -->
<manifest>
    <!-- Requisito de hardware de câmera -->
    <uses-feature android:name="android.hardware.camera.any" />
    
    <!-- Permissões em tempo de execução -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>

Os padrões relacionados para Kotlin Coroutines se aplicam ao envolver chamadas ListenableFuture do CameraX em funções suspend.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

O que o CameraX 1.6 Muda para Apps em Produção

O CameraX 1.6 trouxe três mudanças que afetam implementações existentes:

  1. Backend CameraPipe: A stack de câmera interna agora usa CameraPipe, a mesma arquitetura da câmera Pixel. Isso melhora o desempenho em dispositivos Pixel e fornece uma base mais limpa para recursos avançados. O código existente não precisa de mudanças.

  2. Media3 Muxer por padrão: O VideoCapture agora usa Media3 Muxer em vez de MediaMuxer. Isso melhora a resiliência a crashes: se o app terminar durante a gravação, o arquivo de vídeo permanece reproduzível. A superfície da API permanece inalterada.

  3. API SessionConfig estável: Os apps agora podem configurar ajustes avançados de câmera através do SessionConfig sem anotações experimentais. Isso habilita recursos como uso simultâneo de câmeras frontal/traseira em dispositivos compatíveis.

Caminho de migração: Atualizar a versão da dependência. Testar em dispositivos que anteriormente tinham problemas. As notas de lançamento do CameraX documentam quaisquer mudanças de comportamento entre versões.

  • O CameraX usa vinculação lifecycle-aware: bindToLifecycle() conecta casos de uso tanto ao lifecycle da câmera quanto da Activity
  • Os modos de ImageCapture trocam latência por qualidade: escolher com base no caso de uso, não na capacidade do dispositivo
  • VideoCapture requer configuração do Recorder antes de vincular: preparar as opções de saída antes de iniciar
  • O tratamento de permissões deve explicar o propósito antes de solicitar: reduz as taxas de rejeição
  • O CameraX 1.6 migrou para CameraPipe internamente: nenhuma mudança de API necessária, mas testar em dispositivos-alvo
  • As perguntas de entrevista focam em gerenciamento de lifecycle, combinações de casos de uso e comparação com Camera2
Desafio do dia

Você saberia encontrar o bug em Android?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 11 de setembro de 2026

Compartilhar

Artigos relacionados