【Android】Using Google MLKit & Android X Camera to Implement a Fast QR Code Scanner on Android
If you want to see how the code is written directly, you can: Go to Code Analysis
In this project, Kotlin, Google's MLKit, and the native AndroidX camera are used to implement the QR scanner.

such as zxing, zbar, etc.
Recently, in a project, QA feedback indicated that the current QR code scanner's accuracy was not very high.
So I looked into it and tested it, and finally understood why the accuracy was not very high
.
In fact, it was because:
The project used the old android.hardware.Camera
with zbar
to parse QR codes
and scanned from a side angle or from a distance.
This made the QR code scanner feel slower compared to iOS.
So I decided to try implementing it using Google's MLKit
.
One can dynamically download the module from gms
One uses the module that comes with the library, as shown:
-
I referred to examples and used the new native camera library
androidx.camera
withML Kit
.Compared to the original project using
zbar
withandroid.hardware.Camera
:↪ Both camera libraries are supported from Android API 21 and above.
↪ The version using
zbar
withandroid.hardware.Camera
had three workarounds.I suspect the original project or previous examples used this approach to solve camera initialization issues in the activity.
So they wrote it like this:
↪ After looking into it,
android.hardware.Camera
is not thread-safe and has been @Deprecated.↪ On the other hand, the new
androidx.camera.core.Camera
comes with lifecycle management to handle camera lifecycle issues.This can avoid some strange lifecycle problems.
✅ So I speculate that switching to the new androidx.camera.core.Camera
can more effectively avoid lifecycle issues (because you can control it yourself).
-
The new androidx.camera.core.Camera comes with lifecycle management to handle camera lifecycle issues, avoiding some strange lifecycle problems.
-
The scanner decoding uses MLKit's BarcodeScannerOptions.
It enhances the types of barcodes that can be parsed.
You can refer to this link for the supported formats. Below is the scanner decoding code:
- The overall runtime of the camera on
LG K520 Android 11
did not differ much
In practice, from init camera
to parsing QR code
takes about 1 second
However, it can avoid the camera crash risk caused by lifecycle
- The actual test shows a slightly faster response speed compared to the original solution (not noticeable on better phones, minimal difference)
For example, zbar
+ android.hardware.Camera
will completely focus
before starting to decode the QR code
mlkit
+ androidx.camera.core.Camera
can start decoding the QR code halfway through focusing
- The image on the left uses
zbar
, and the right usesML kit
(this gif is slowed down to0.25x
) androidx.camera.core.Camera
can handle lifecycle-related issues
Slowing down the new solution to 0.25x
makes it feel like: waiting for a while before displaying the camera
(right image)
- However, the noticeable improvement is that it is easier to scan when using odd angles or when the camera is farther away
The difference between setting the camera target resolution or not is higher success rate of scanning from a distance
Additionally
: the API Java doc mentions- The default resolution of the camera without TargetResolution set is
640*480
- After setting, there will be a priority to choose the actual
Resolution
- Here is the outline: Priority is given to resolutions close to and greater than the Target Resolution > Priority is given to resolutions close to and smaller than the Target Resolution > The ratio of the resolution will be primarily determined by the provided
Size
- Therefore, it is speculated that due to
hardware limitations
, thedistance
that can be resolved may vary for each device
- The default resolution of the camera without TargetResolution set is
They reported that the accuracy of the app has indeed improved compared to the previous version
Upon further inquiry, they said the success rate of scanning from the side
or from a farther distance
or being detected
has indeed increased
Thus... a problem was solved XD
I have placed the source code at the top of this article
If interested, you can clone it directly and modify it
- Below I will share the main code analysis of the QR code scanner
Feel free to refer to it if interested
Code Explanation
The BaseCameraLifecycleObserver class is mainly used for inheritance by othersIt contains some basic camera initialization content
It also handles some lifecycle-related content
(e.g., handling the camera according to the lifecycle)
It also provides external methods to manually turn the camera on or off
cameraProviderDeferred
Worth mentioning is the use of cameraProviderDeferred hereIt is used to obtain ProcessCameraProvider
Since this operation is time-consuming, asynchronous operation is used for more flexibility
(However, I have also tried directly instantiating ProcessCameraProvider
In most cases, it is not easy to encounter ANR or stuck situations
Only occasionally 1 or 2 times on older models, such as Android 5.0)
The concept here is
CompletableDeferred uses .apply
Within the apply scope, it creates ProcessCameraProvider
Use await()
to wait where needed
Achieving asynchronous operations while waiting for the actual creation before proceeding
The difference between completableDeferred and regular Deferred is
It can be manually controlled to complete using complete()
Which is suitable for this example
Need to wait for the ProcessCameraProvider listener to return
To know that initialization is complete
Code Explanation
ScannerLifecycleObserver is relatively simpleInherits from BaseCameraLifecycleObserver
Mainly writes features that might be needed based on project requirements
Such as some extended content
Like getCameraDisplayOrientation
Can be used to adjust the supported resolution of the camera
Or getImageAnalysis to get ImageAnalysis
ImageAnalysis is mainly used in MLKit to set which barcode types can be parsed
Code Explanation
ScannerAnalyzer is used to implement barcode parsingInherits from BaseCameraLifecycleObserver
Since we are parsing QR codes, we use FORMAT_QR_CODE