Android CameraX en 2026 : Capture Photo et Vidéo avec Questions d'Entretien
Guide complet CameraX 1.6 pour Android : architecture lifecycle-aware, capture photo, enregistrement vidéo, intégration Jetpack Compose et questions d'entretien technique.

CameraX simplifie le développement caméra Android en abstrayant la complexité matérielle derrière une API lifecycle-aware. Avec la version 1.6.2 désormais stable et la migration vers CameraPipe achevée, CameraX offre la même pile caméra haute performance qui équipe l'application appareil photo Pixel.
CameraX 1.6 a migré vers CameraPipe, utilise Media3 Muxer par défaut pour VideoCapture et a stabilisé l'API SessionConfig. Les builders DSL Kotlin (preview { }, imageCapture { }, videoCapture { }) réduisent le boilerplate de 40%.
Architecture CameraX et Modèle de Cas d'Usage
CameraX fournit quatre cas d'usage principaux qui peuvent s'exécuter simultanément : Preview, ImageCapture, ImageAnalysis et VideoCapture. Chaque cas d'usage se lie à un LifecycleOwner, de sorte que la caméra démarre et s'arrête automatiquement avec l'activité ou le fragment.
Le CameraProvider agit comme une factory qui instancie ces cas d'usage et les lie à la caméra. Ce découplage signifie que le même code fonctionne sur des appareils de différents fabricants sans se soucier des particularités spécifiques à chaque constructeur.
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 lie les cas d'usage au lifecycle
cameraProvider = ProcessCameraProvider.getInstance(context).await()
}
fun bindUseCases(previewView: PreviewView) {
// Délier les cas d'usage existants avant de relier
cameraProvider.unbindAll()
// Cas d'usage Preview affiche le flux caméra
val preview = Preview.Builder()
.build()
.apply { setSurfaceProvider(previewView.surfaceProvider) }
// ImageCapture pour les photos
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// VideoCapture avec Recorder
val recorder = Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.FHD))
.build()
videoCapture = VideoCapture.withOutput(recorder)
// Lier tous les cas d'usage au lifecycle
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture,
videoCapture
)
}
}L'appel bindToLifecycle connecte les cas d'usage à la fois au matériel caméra et au lifecycle. Lorsque le LifecycleOwner entre dans l'état STARTED, la caméra s'ouvre. Lorsqu'il entre dans l'état STOPPED, la caméra se ferme automatiquement.
Implémentation de la Capture Photo avec ImageCapture
ImageCapture propose deux modes de capture : CAPTURE_MODE_MINIMIZE_LATENCY pour une réponse rapide du déclencheur, et CAPTURE_MODE_MAXIMIZE_QUALITY pour les prises de vue nécessitant un traitement intensif. Le choix dépend de la priorité de l'application : vitesse (applications sociales) ou qualité (scanners de documents).
class PhotoCaptureManager(
private val context: Context,
private val imageCapture: ImageCapture
) {
private val mainExecutor = ContextCompat.getMainExecutor(context)
fun capturePhoto(onResult: (Uri?) -> Unit) {
// Générer un nom de fichier unique avec horodatage
val filename = "IMG_${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
// Stocker dans Pictures/CameraX sur 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 contient l'URI MediaStore
onResult(results.savedUri)
}
override fun onError(exception: ImageCaptureException) {
Log.e("PhotoCapture", "Échec de capture : ${exception.message}")
onResult(null)
}
}
)
}
}CameraX 1.5 a ajouté le support de capture DNG (RAW) pour les applications nécessitant des données brutes du capteur. Il convient de vérifier les capacités de l'appareil avec ImageCapture.getImageCaptureCapabilities() avant d'activer la sortie RAW.
Enregistrement Vidéo avec VideoCapture et Recorder
VideoCapture utilise un objet Recorder pour gérer l'encodage et le muxing. L'API Recorder suit un pattern prepare-start-stop qui gère automatiquement la synchronisation audio.
class VideoRecordingManager(
private val context: Context,
private val videoCapture: VideoCapture<Recorder>
) {
private var activeRecording: Recording? = null
fun startRecording(onEvent: (VideoRecordEvent) -> Unit): Boolean {
// Vérifier si un enregistrement est déjà en cours
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()
// Préparer l'enregistrement avec audio si permission accordée
val pendingRecording = videoCapture.output
.prepareRecording(context, mediaStoreOutput)
if (hasAudioPermission()) {
pendingRecording.withAudioEnabled()
}
// Démarrer l'enregistrement et stocker la référence
activeRecording = pendingRecording.start(
ContextCompat.getMainExecutor(context)
) { event ->
onEvent(event)
// Effacer la référence quand l'enregistrement termine
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 a ajouté le support vidéo au ralenti. Les capacités peuvent être interrogées avec Recorder.getHighSpeedVideoCapabilities() avant d'activer l'enregistrement à 120fps ou 240fps.
Prêt à réussir tes entretiens Android ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
CameraX avec Jetpack Compose
CameraX 1.5 a introduit l'artifact camera-compose avec un composable CameraXViewfinder. Pour les applications utilisant encore PreviewView basé sur View, AndroidView fournit l'interopérabilité.
@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)
}
)
// Boutons de capture en overlay
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(32.dp),
horizontalArrangement = Arrangement.spacedBy(24.dp)
) {
IconButton(
onClick = { viewModel.capturePhoto() }
) {
Icon(Icons.Default.Camera, "Prendre une photo")
}
IconButton(
onClick = { viewModel.toggleRecording() }
) {
Icon(
if (viewModel.isRecording) Icons.Default.Stop
else Icons.Default.Videocam,
"Enregistrer une vidéo"
)
}
}
}
}Pour les implémentations Compose-first, le guide sur les questions d'entretien Jetpack Compose couvre les patterns d'intégration ViewModel qui s'appliquent directement à la gestion d'état de la caméra.
Gestion des Permissions Caméra
Android requiert la permission CAMERA au runtime. RECORD_AUDIO n'est nécessaire que lors de l'enregistrement vidéo avec son. Le flux de permission doit expliquer pourquoi l'accès est nécessaire avant de le demander.
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)
}
}
}Questions d'Entretien CameraX
CameraX apparaît dans les entretiens Android lors de discussions sur l'intégration caméra, la gestion du lifecycle ou l'abstraction matérielle. Ces questions testent la compréhension de l'architecture et les détails d'implémentation pratiques.
Pourquoi utiliser CameraX plutôt que Camera2 ?
CameraX abstrait les particularités spécifiques aux appareils que Camera2 expose directement. Une implémentation Camera2 nécessite de gérer des centaines de contournements spécifiques aux appareils. CameraX intègre ces corrections dans la bibliothèque, testées contre le CameraX Test Lab qui couvre plus de 150 modèles d'appareils.
La liaison lifecycle-aware élimine la gestion manuelle des ressources. Camera2 nécessite des appels explicites open/close qui provoquent des fuites mémoire s'ils ne sont pas gérés correctement dans chaque callback de lifecycle.
Comment CameraProvider lie-t-il les cas d'usage au lifecycle ?
CameraProvider utilise le pattern LifecycleObserver en interne. Lors de l'appel à bindToLifecycle(), il enregistre un observateur sur le LifecycleOwner fourni. Cet observateur reçoit les événements ON_START et ON_STOP pour ouvrir et fermer la caméra.
Le point clé : CameraX ne démarre pas la caméra immédiatement au binding. Il attend que le lifecycle atteigne l'état STARTED, ce qui correspond au timing de Activity.onStart().
Que se passe-t-il lors de la liaison de cas d'usage incompatibles ?
Toutes les combinaisons de cas d'usage ne fonctionnent pas sur tous les appareils. Preview + VideoCapture + ImageAnalysis + ImageCapture peut échouer sur du matériel ancien. CameraX lance une IllegalArgumentException au moment du binding, pas au runtime.
La solution consiste à vérifier CameraProvider.hasCamera() et les capacités de l'appareil avant de lier des combinaisons complexes. Pour les applications critiques, tester explicitement sur des appareils d'entrée de gamme.
Expliquer la différence entre CAPTURE_MODE_MINIMIZE_LATENCY et CAPTURE_MODE_MAXIMIZE_QUALITY
MINIMIZE_LATENCY réduit le délai du déclencheur en utilisant des pipelines de traitement plus rapides, en sautant possiblement certains post-traitements. MAXIMIZE_QUALITY applique le traitement complet HDR+, la réduction de bruit et la capture multi-frames sur les appareils supportés.
Le compromis : le mode latence capture en 100-200ms, le mode qualité peut prendre 500ms-1s selon l'éclairage. Les applications sociales choisissent typiquement la latence. Les applications de scan de documents choisissent la qualité.
Comment CameraX gère-t-il la rotation de l'appareil ?
CameraX gère automatiquement la rotation via le paramètre target rotation. Par défaut, il utilise la rotation du Display, mais les applications peuvent surcharger avec setTargetRotation(). Les données EXIF de l'image de sortie contiennent le tag d'orientation correct.
Pour PreviewView, le scaleType gère les différences de ratio d'aspect. FILL_CENTER recadre pour remplir, FIT_CENTER affiche des bandes noires. Le choix dépend de si l'application privilégie l'aperçu plein écran ou évite le recadrage.
Dépendances et Configuration pour CameraX 1.6
La bibliothèque CameraX est divisée en plusieurs artifacts. Inclure uniquement ce que l'application utilise pour minimiser la taille de l'APK.
dependencies {
val cameraxVersion = "1.6.2"
// Bibliothèque core requise pour tous les cas d'usage
implementation("androidx.camera:camera-core:$cameraxVersion")
// Implémentation Camera2 (requise)
implementation("androidx.camera:camera-camera2:$cameraxVersion")
// Intégration Lifecycle
implementation("androidx.camera:camera-lifecycle:$cameraxVersion")
// Cas d'usage VideoCapture
implementation("androidx.camera:camera-video:$cameraxVersion")
// PreviewView et composants UI caméra
implementation("androidx.camera:camera-view:$cameraxVersion")
// Optionnel: Extensions (Mode nuit, HDR, etc.)
implementation("androidx.camera:camera-extensions:$cameraxVersion")
// Optionnel: Effets temps réel
implementation("androidx.camera:camera-effects:$cameraxVersion")
}
android {
// CameraX nécessite le bytecode Java 11
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}Le Manifest nécessite les déclarations de fonctionnalités et les permissions :
<!-- AndroidManifest.xml -->
<manifest>
<!-- Exigence matérielle caméra -->
<uses-feature android:name="android.hardware.camera.any" />
<!-- Permissions runtime -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>Les patterns associés pour Kotlin Coroutines s'appliquent lors de l'encapsulation des appels ListenableFuture de CameraX dans des fonctions suspend.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Ce que CameraX 1.6 Change pour les Applications en Production
CameraX 1.6 a apporté trois changements qui affectent les implémentations existantes :
-
Backend CameraPipe : La pile caméra interne utilise maintenant CameraPipe, la même architecture que la caméra Pixel. Cela améliore les performances sur les appareils Pixel et fournit une fondation plus propre pour les fonctionnalités avancées. Le code existant n'a pas besoin de modifications.
-
Media3 Muxer par défaut : VideoCapture utilise maintenant Media3 Muxer au lieu de MediaMuxer. Cela améliore la résilience aux crashs : si l'application se termine pendant l'enregistrement, le fichier vidéo reste lisible. La surface d'API reste inchangée.
-
API SessionConfig stable : Les applications peuvent maintenant configurer des paramètres caméra avancés via SessionConfig sans annotations expérimentales. Cela permet des fonctionnalités comme l'utilisation simultanée des caméras avant/arrière sur les appareils supportés.
Chemin de migration : Mettre à jour la version de dépendance. Tester sur les appareils qui présentaient auparavant des problèmes. Les notes de version CameraX documentent tout changement de comportement entre versions.
- CameraX utilise la liaison lifecycle-aware :
bindToLifecycle()connecte les cas d'usage à la fois au lifecycle de la caméra et de l'Activity - Les modes ImageCapture échangent latence contre qualité : choisir en fonction du cas d'usage, pas des capacités de l'appareil
- VideoCapture nécessite la configuration du Recorder avant binding : préparer les options de sortie avant de démarrer
- La gestion des permissions doit expliquer le but avant de demander : réduit les taux de refus
- CameraX 1.6 a migré vers CameraPipe en interne : aucun changement d'API requis, mais tester sur les appareils cibles
- Les questions d'entretien se concentrent sur la gestion du lifecycle, les combinaisons de cas d'usage et la comparaison avec Camera2
Tu saurais repérer le bug en Android ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 11 septembre 2026
Partager
Articles similaires

Modularisation Android en 2026 : Architecture Multi-Module et Questions d'Entretien
Guide complet sur la modularisation Android en 2026. Découvrez comment structurer une architecture multi-module, utiliser les catalogues de versions Gradle, et les questions d'entretien technique essentielles.

Jetpack Navigation Compose en 2026 : Navigation Type-Safe et Questions d'Entretien
Maîtrisez Jetpack Navigation Compose avec la navigation type-safe en Kotlin. Guide complet avec exemples de code, gestion des arguments et questions d'entretien Android fréquentes en 2026.

Android WorkManager en 2026 : Tâches en Arrière-plan, Contraintes et Questions d'Entretien
Guide complet sur WorkManager pour Android en 2026 : création de Workers, contraintes d'exécution, chaînage de tâches et questions techniques d'entretien.