Overview
In Google IO 2019, Google introduced a powerful API to deal with camera tasks, called CameraX. This API is a part of Jetpack. It minimizes camera development time with consistent and easy to use API. It is a simplified API for handling common camera functionality across majority of android devices right from Android 5.0 – Lollipop (API Level 21) to the latest version.
Introduction
Everyone knows that Camera development is not an easy task. When I say camera development is, it means you have to provide a consistent experience across a range of devices. CameraX provide backward compatibility. It works on Lollipop and higher version of Android devices. it is a use-case based approach which is app-lifecycle aware. It comes with three use cases that cover all functionality which is required for Camera App development.
What’s New In CameraX
Backward compatible:
CameraX is backward compatible with Android 5.0 / Lollipop (API 21) and it works on 90% of devices in the market today.
Consistent behaviour:
CameraX provide the same consistency as camera1 via the camera2 API Legacy layer. Uptil now, we had to do a lot of manually code to provide consistency across a variety of devices.
Fixed a lot of issues across the device:
Some of the issue fix in cameraX.
- Front/Back camera switch crashes
- Optimised camera closures
- Orientation incorrect
- Flash not firing
Here is the typical walkthrough of the camera app architecture.
Uptil now, Camera app used to communicate with the public Camera2 API and the camera2 API handled communication with device HAL (Hardware abstraction layer).
Now we only have to deal with cameraX as it hides all the lower level complex from the developers.
Let’s Do Some Practical
Requirements:
- Android API level 21
- Android Architecture Components 1.1.1
Add below Dependencies:
def camerax_version = "1.0.0-alpha03" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}"
Add Camera permission in AndroidManifest:
<uses-permission android:name="android.permission.CAMERA" />
CameraX launch with three use-case.
- Preview
- Image analysis
- Image capture
Here we are briefly talking about use-cases:
1. Preview:
Build a preview config using a builder.
val previewConfig = PreviewConfig.Builder().apply { setTargetAspectRatio(Rational(1, 1)) setTargetResolution(Size(640, 640)) }.build()
Configure the preview.
val preview = Preview(previewConfig)
Set PreviewOutListener. What’s going on now when the preview becomes active, It’s going to output a preview output.
preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // To update the SurfaceTexture }
Next step is just to turn it on and turn off. So binding the preview use-case to activity lifecycle. It means when an activity starts, the preview is going to start and the camera is going to start to stream. And when activity stops, the preview is going to stop and the camera is going to shut down.
CameraX.bindToLifecycle(this as LifecycleOwner, preview)
Note: If you face any issue after adding the line of code mentioned above, you’ll need to add the following dependency as well.
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
Here instead of activity lifecycle we can also use other lifecycle such as the fragment lifecycle.
There would need to be code for setting up permission, attaching to views, and managing the surface texture.
2. Image analysis:
CameraX provide easy access to the buffers from the camera, so you can perform your own analysis.
Step same as preview use-case to create an Image analysis config object.
val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build()
The above step is to request a resolution. Now let’s say your processing requires some minimum resolution for it to succeed. That you’ll specify here.
TheCameraX is going to balance the requests from your application with the device capability. So if you’re able to get the target resolution, you’ll get it Otherwise cameraX would try the next higher resolution, and if failing to get request resolution then cameraX will fall back to a 640 x 480 resolution, which is guaranteed across all devices.
val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer { image: ImageProxy, rotationDegrees: Int -> // write your code here. }
Image analysers provide all information required for image processing.
We have to bind image analysis use case to lifecycle, so add imageAnalysis to CameraX.bindToLifecycle(…) method.
CameraX.bindToLifecycle(this as LifecycleOwner, preview, imageAnalysis)
3. Image capture:
Image capture allows you to take a high-quality picture with the camera.
Create an Image capture config object.
val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetRotation(windowManager.defaultDisplay.rotation) .build() val imageCapture = ImageCapture(imageCaptureConfig) CameraX.bindToLifecycle(this as LifecycleOwner, preview, imageAnalysis, imageCapture)
We know getting rotation on devices can be hard, getting portrait mode and landscape mode just right on a variety of devices is harder. But in cameraX this problem is reduced.
Now it is ready to go. Preview will be displayed on screen, the analyzer will be running, and application-ready to take a picture.
But, What about attaching to output?
It simply requires to call tackPicture method on image-capture use case on button click.
fun captureImage(view: View) { val file = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg") imageCapture.takePicture(file, object : ImageCapture.OnImageSavedListener { override fun onError( error: ImageCapture.UseCaseError, message: String, exc: Throwable? ) { val msg = "Photo capture failed: $message" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.e("CameraXApp", msg) exc?.printStackTrace() } override fun onImageSaved(file: File) { val msg = "Photo capture succeeded: ${file.absolutePath}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d("CameraXApp", msg) } }) }
In this method we need to pass the destination of image where the image will be stored after capturing.
You can also attach a listener to listen success and failure events.
Benefits Of CameraX
Easy to use:
CameraX has made all the three ( Preview, Image analysis, and Image capture) use cases activity lifecycle aware, so you don’t have to start and stop the camera separately anymore.
- Reduced device-specific testing
- 75% reduction in lines of code
- Smaller app size
It has hidden many of the details of Camera2.
- Opening the camera
- Creating the session
- Preparing the correct surfaces
- Selecting the resolutions
- and the careful shutdown that you sometimes
Conclusion
Having personally experienced old school camera development on Android, I appreciate the cameraX API. Because of its lifecycle awareness, developer has no need to worry about when to turn the camera on and when to turn it off. Using this API you can make the camera App with a lot less code.