At this year’s Google I/O conference, Google unveiled the New CameraX support pack, which officially does

help you make camera app development easier

Google released the Camera2 API a few years ago to replace Camera1. Most developers still prefer to use the Camera1 API because the API is difficult to use and because it is compatible with s devices prior to 5.0. Combined with the critically acclaimed Archecture Component package Lifecycle automatically manages the camera Lifecycle so developers no longer have to worry about when to turn the camera on and when to release it. Even contact various mobile phone manufacturers to provide a unified API call using beauty and other interfaces.

In this demo, we will use two of the three main functions of cameraX:

  • preview
  • analysis

To create the app

Create app and declare camera permissions

<?xml version="1.0" encoding="utf-8"? >
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools" package="github.hotstu.camerax.qrcodec">

    <uses-permission android:name="android.permission.CAMERA"/>

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>
Copy the code

Adding dependencies

    implementation 'com. Tbruyelle. Rxpermissions2: rxpermissions: 0.9.5'
    def camerax_version = "1.0.0 - alpha02"
    implementation "androidx.camera:camera-core:${camerax_version}"
    // If you want to use Camera2 extensions.
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation 'com. Google. Zxing: core: 3.3.2 rainfall distribution on 10-12'
Copy the code

Core.core: Core :1.1.0-rc1 AppcompatActivity is not LifeCycleOwner. However, Camerax relies on LifeCycleOwner to manage lifecycles, so the golden code for resolving dependency conflicts in the support Liabary era is:

subprojects {
    configurations.all {
        resolutionStrategy {
            force "Androidx. Core: the core: 1.0.2."}}}Copy the code

Write code to achieve camera preview

        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(Rational(1.1))
            setTargetResolution(Size(640.640))
        }.build()
        val preview = Preview(previewConfig)
       preview.setOnPreviewOutputUpdateListener {
            textureView.surfaceTexture = it.surfaceTexture
        }
        CameraX.bindToLifecycle(this@MainActivity, preview)
Copy the code

Write code to implement data analysis callbacks

Data analysis can specify the thread of the callback

        val analysisConfig = ImageAnalysisConfig.Builder().apply {
            setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
            val analyzerThread = HandlerThread("BarcodeAnalyzer").apply { start() }
            setCallbackHandler(Handler(analyzerThread.looper))
        }.build()

        val analysis = ImageAnalysis(analysisConfig)
        analysis.analyzer = QRcodeAnalyzer()

        CameraX.bindToLifecycle(this@MainActivity, analysis)
Copy the code

Bind preview and analysis at the same time, and handle permission requests

class MainActivity : AppCompatActivity() {

    @SuppressLint("CheckResult")
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(Rational(1.1))
            setTargetResolution(Size(640.640))
        }.build()

        val analysisConfig = ImageAnalysisConfig.Builder().apply {
            setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
            val analyzerThread = HandlerThread("BarcodeAnalyzer").apply { start() }
            setCallbackHandler(Handler(analyzerThread.looper))
        }.build()

        val rxPermissions = RxPermissions(this)
        val preview = Preview(previewConfig)
        val analysis = ImageAnalysis(analysisConfig)

        preview.setOnPreviewOutputUpdateListener {
            textureView.surfaceTexture = it.surfaceTexture
        }

        analysis.analyzer = QRcodeAnalyzer()
        rxPermissions.request(android.Manifest.permission.CAMERA)
                .subscribe {
                    CameraX.bindToLifecycle(this@MainActivity,
                            preview,
                            analysis)
                }
    }
}
Copy the code

Implement QRcodeAnalyzer

To do qr code decoding, many developers are directly based on the Zxing-Android project to delete and modify, in fact, it is not recommended to do so, because the project started from the early version of Android, some things are to support the old version of the system and make compromises, a lot of code is old and not elegant, Look at the source code and you will find that it calls the camera method is very complex. Here we just use the Xzing core library com.google.zxing:core

class QRcodeAnalyzer : ImageAnalysis.Analyzer {
    private val reader: MultiFormatReader = MultiFormatReader()

    init {
        val map = mapOf<DecodeHintType, Collection<BarcodeFormat>>(
            Pair(DecodeHintType.POSSIBLE_FORMATS, arrayListOf(BarcodeFormat.QR_CODE))
        )
        reader.setHints(map)
    }

    override fun analyze(image: ImageProxy, rotationDegrees: Int) {
        if(ImageFormat.YUV_420_888 ! = image.format) { Log.e("BarcodeAnalyzer"."expect YUV_420_888, now = ${image.format}")
            return
        }
        val buffer = image.planes[0].buffer
        val data = ByteArray(buffer.remaining())
        val height = image.height
        val width = image.width
        buffer.get(data)
        //TODO adjusts the rectangular area of Crop, which is now full screen (full screen has better recognition experience, but may be OOM on some phones)
        val source = PlanarYUVLuminanceSource(data, width, height, 0.0, width, height, false)

        val bitmap = BinaryBitmap(HybridBinarizer(source))

        try {
            val result = reader.decode(bitmap)
            Log.e("BarcodeAnalyzer"."resolved!!! = $result")}catch (e: Exception) {
            Log.d("BarcodeAnalyzer"."Error decoding barcode")}}}Copy the code

You’re done. No more than 100 lines of core code.

conclusion

To summarize, cameraX is still a bit of a work in progress, dealing with the camera’s rotation, for example, is still a bit of a puzzle.

Project address: github.com/hotstu/QRCo…

The content I wrote in nuggets is my own original, unless otherwise indicated, reprint with my consent, welcome to share, please indicate the source.