Camera2 offers granular control but introduces boilerplate: managing
`CameraDevice` states, `CameraCaptureSession` lifecycles, background threads,
`HandlerThread`, and manual orientation calculations.
CameraX simplifies this by binding high-level `UseCase`s such as `Preview`,
`ImageCapture`, and `ImageAnalysis` directly to Android lifecycles, handling
thread management and device-specific workarounds automatically.
*** ** * ** ***
## Remove Camera2 boilerplate
Migrating to CameraX removes manual setup code:
1. **Delete manual thread handling** : Remove `HandlerThread`, `Handler`, and executors dedicated to camera background tasks. CameraX manages its own threads.
2. **Remove session and device callbacks** : Delete `CameraDevice.StateCallback` and `CameraCaptureSession.StateCallback` implementations.
3. **Remove surface management** : Remove manual routing of `Surface` objects from `TextureView` or `SurfaceView` to the camera device.
4. **Remove manual orientation logic** : Delete calculations involving `CameraCharacteristics.SENSOR_ORIENTATION` and display rotation for adjusting preview and capture orientation.
*** ** * ** ***
## Initialize `ProcessCameraProvider`
Request the `ProcessCameraProvider` and bind your use cases to the
`LifecycleOwner` activity or fragment. This replaces the
`CameraManager.openCamera` flow.
```kotlin
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(context, lifecycleOwner) {
val cameraProvider = ProcessCameraProvider.getInstance(context).await()
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// ImageAnalysis is common when migrating from Camera2 ImageReader
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
cameraProvider.unbindAll()
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture,
imageAnalysis
)
}
```
*** ** * ** ***
## Implement the preview and tap-to-focus
CameraX handles surface configuration automatically. Choose based on your UI
toolkit:
### Option A: For Android Views
Use `androidx.camera.view.PreviewView` in your layout, and bind it to the
`Preview` use case.
```kotlin
preview.setSurfaceProvider(previewView.surfaceProvider)
```
### Option B: For Jetpack Compose
Use `androidx.camera.compose.CameraXViewfinder`.
```kotlin
var surfaceRequest by remember { mutableStateOf(null) }
val preview = remember {
Preview.Builder().build().apply {
setSurfaceProvider { request -> surfaceRequest = request }
}
}
```
*** ** * ** ***
## Capture a photo
Replace `ImageReader` capture flows and
`CaptureRequest.Builder.TEMPLATE_STILL_CAPTURE` with the `ImageCapture` use
case. CameraX handles the rotation natively using the returned `ImageProxy`.
```kotlin
imageCapture.takePicture(
cameraExecutor,
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
if (bitmap != null) {
val matrix = Matrix()
matrix.postRotate(image.imageInfo.rotationDegrees.toFloat())
if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
matrix.postScale(-1f, 1f)
}
val rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
}
// MUST close proxy
image.close()
}
override fun onError(exception: ImageCaptureException) {
Log.e("CameraX", "Capture failed: ${exception.message}", exception)
}
}
)
```
*** ** * ** ***
## Implement image analysis
If you were using `ImageReader` in Camera2 to access raw frames, e.g., for QR
scanning or ML,, replace it with the CameraX `ImageAnalysis` use case.
```kotlin
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
try {
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
// Process image here (e.g., run object detection)
// ...
} finally {
// MUST close the imageProxy to avoid blocking the pipeline
imageProxy.close()
}
}
```
*** ** * ** ***
## Use Camera2 interop
If your app requires specific Camera2 configuration options, such as custom
exposure modes or flash settings, that aren't exposed directly in CameraX,
use `Camera2Interop` to apply them to your CameraX use cases.
```kotlin
// Use Camera2Interop to set Camera2-specific capture options
val extender = Camera2Interop.Extender(imageCaptureBuilder)
extender.setCaptureRequestOption(
CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_OFF
).setCaptureRequestOption(
CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH
)
```
*** ** * ** ***
## Follow constraints
- **Don't open the CameraDevice manually** : Let CameraX manage device opening and closing using `bindToLifecycle`.
- **Don't forget to close `ImageProxy`** : In both `ImageCapture` and `ImageAnalysis` callbacks, you **must** call `image.close()`. Failure to do so will block the camera pipeline.
- **Don't block the ImageAnalysis thread** : The analyzer runs frame-by-frame. If you need to perform heavy computations, offload them to a background worker and close the `ImageProxy` as soon as possible.