Android CameraX QR 扫描仪仅在模拟器中工作,不适用于物理设备

发布于 2025-01-16 16:18:34 字数 11849 浏览 1 评论 0原文

我正在为一个学校项目开发一个应用程序,它涉及扫描二维码。我已经使用 Android 模拟器上的 CameraX 库完美地完成了 QR 码扫描,但是当我导出 APK 并将其安装到我的手机 (LG Velvet) 上时,它不会扫描任何代码。相机仍然拉起,我可以看到预览,但它似乎无法识别代码。

这是我的 build.gradle 文件:

plugins {
    id 'com.android.application'
    id 'com.google.gms.google-services'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.qrhunter"
        minSdk 24
        targetSdk 32
        versionCode 1
        versionName "1.0"
        multiDexEnabled true;

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    implementation 'com.google.firebase:firebase-firestore:21.4.0'
    implementation 'com.google.firebase:firebase-auth:20.0.4'
    implementation 'com.google.firebase:firebase-storage:20.0.1'
    implementation "io.grpc:grpc-okhttp:1.32.2"
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    //Camera Dependencies
    def camerax_version = "1.1.0-beta01"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-view:${camerax_version}"

    //Zebra Crossing Dependencies
    implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
}

这也是我的 AndroidManifest.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.qrhunter">

    <uses-feature android:name="android.hardware.camera.any" />

    <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/Theme.QRHunter">
        <activity
            android:name=".Login"
            android:exported="false" />
        <activity
            android:name=".Register"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".qrAddObjectPictureActivity"
            android:exported="false" />
        <activity
            android:name=".qrScannedGetInfoActivity"
            android:exported="false" />
        <activity
            android:name=".qrScanCameraActivity"
            android:exported="false" />

        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />
    </application>

</manifest>

这是我初始化相机和 QR 分析器的代码

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutionException;

/**
 * Activity for running and managing the camera used to scan QR Codes
 * Code for camera usage and qr scanning referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
 */
public class qrScanCameraActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CAMERA = 0;

    private PreviewView cameraPreviewView;
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;

    private Button qrCodeFoundButton;
    private String qrCode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        cameraPreviewView = findViewById(R.id.cameraPreviewView);
        qrCodeFoundButton = findViewById(R.id.cameraActivity_qrCodeFoundButton);

        qrCodeFoundButton.setVisibility(View.INVISIBLE);
        qrCodeFoundButton.setOnClickListener(new View.OnClickListener() {
            // onClick function for the QR Code Found Button
            @Override
            public void onClick(View view){
                Log.i(qrScanCameraActivity.class.getSimpleName(), "QR Code Found: " + qrCode);
                Intent qrScannedIntent = new Intent(qrScanCameraActivity.this, qrScannedGetInfoActivity.class);
                qrScannedIntent.putExtra("QR_Content", qrCode);
                qrScannedIntent.putExtra("entryFromScanBool", true);
                startActivity(qrScannedIntent);
            }
        });

        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        requestCamera();
    }

    /**
     * Requests permission to use the camera in the application
     */
    private void requestCamera() {
        if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
            startCamera();
        }else{
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)){
                ActivityCompat.requestPermissions(qrScanCameraActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            }else{
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            }
        }
    }

    @SuppressLint("MissingSuperCall") //Do not need the super method call unless dealing with fragments
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
        if(requestCode == PERMISSION_REQUEST_CAMERA){
            if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //if permissions were granted start camera
                startCamera();
            }else{
                Toast.makeText(this, "Camera Permission Denied, please allow camera use in settings", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**
     * start the camera and bind the preview correctly
     * catch any exceptions
     */
    private void startCamera(){
        cameraProviderFuture.addListener(() -> {
            try{
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindCameraPreview(cameraProvider);
            }catch(ExecutionException | InterruptedException e) { //catch any errors when starting the camera
                Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    /**
     * binds the created camera to the camera preview view in order to allow user to see camera view
     * set the analyzer for the camera to detect QR Codes
     * @param cameraProvider object representing the Camera
     */
    private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider){
        cameraPreviewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);

        Preview preview = new Preview.Builder().build();

        CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();

        preview.setSurfaceProvider(cameraPreviewView.getSurfaceProvider());

        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(new Size(1280,720))
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();

        imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeListener() {
            @Override
            public void qrCodeFound(String _qrCode) {
                qrCode = _qrCode;
                qrCodeFoundButton.setVisibility(View.VISIBLE);
            }

            @Override
            public void qrCodeNotFound() {
                qrCodeFoundButton.setVisibility(View.INVISIBLE);
            }
        }));

        Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
    }

    @Override
    public void onBackPressed() {
        finish();
    }

}

我不确定问题还存在于哪里,因为所有代码都可以完美运行。我的手机在 Android 11、API 30 上运行,我的模拟器也在同一设备上运行(Pixel XL、API 30、Android 11) 对此的任何帮助将不胜感激

编辑:添加分析器代码

/**
 * Class for analyzing frames of camera to check for QR Code, and decode QR Code
 * Code is referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
 */
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
    private QRCodeListener listener;

    /**
     * Constructor for QRCodeImageAnalyzer
     * @param listener
     */
    public QRCodeImageAnalyzer(QRCodeListener listener){
        this.listener = listener;
    }

    @Override
    public void analyze(@NonNull ImageProxy image){
        if(image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888){
            ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
            byte[] imageData = new byte[byteBuffer.capacity()];
            byteBuffer.get(imageData);

            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, image.getWidth(), image.getHeight(),
                    0, 0, image.getWidth(), image.getHeight(), false);

            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

            try{
                Result result = new QRCodeMultiReader().decode(binaryBitmap);
                listener.qrCodeFound(result.getText());
            }catch(FormatException | ChecksumException | NotFoundException e) {
                listener.qrCodeNotFound();
            }
        }
        image.close();
    }
}

I am developing an app for a school project and it involves scanning QR Codes. I have gotten the QR Code scanning working perfectly using the CameraX library on the Android emulator, but when I export my APK and install it on my phone (LG Velvet) it will not scan any code. The camera still pulls up and I can see the preview, but it seemingly cannot recognize a code.

Here is my build.gradle file:

plugins {
    id 'com.android.application'
    id 'com.google.gms.google-services'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.qrhunter"
        minSdk 24
        targetSdk 32
        versionCode 1
        versionName "1.0"
        multiDexEnabled true;

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    implementation 'com.google.firebase:firebase-firestore:21.4.0'
    implementation 'com.google.firebase:firebase-auth:20.0.4'
    implementation 'com.google.firebase:firebase-storage:20.0.1'
    implementation "io.grpc:grpc-okhttp:1.32.2"
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    //Camera Dependencies
    def camerax_version = "1.1.0-beta01"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-view:${camerax_version}"

    //Zebra Crossing Dependencies
    implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
}

Here is my AndroidManifest.xml file as well

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.qrhunter">

    <uses-feature android:name="android.hardware.camera.any" />

    <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/Theme.QRHunter">
        <activity
            android:name=".Login"
            android:exported="false" />
        <activity
            android:name=".Register"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".qrAddObjectPictureActivity"
            android:exported="false" />
        <activity
            android:name=".qrScannedGetInfoActivity"
            android:exported="false" />
        <activity
            android:name=".qrScanCameraActivity"
            android:exported="false" />

        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />
    </application>

</manifest>

Here is the code where I am initializing the camera and the QR analyzer

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutionException;

/**
 * Activity for running and managing the camera used to scan QR Codes
 * Code for camera usage and qr scanning referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
 */
public class qrScanCameraActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CAMERA = 0;

    private PreviewView cameraPreviewView;
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;

    private Button qrCodeFoundButton;
    private String qrCode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        cameraPreviewView = findViewById(R.id.cameraPreviewView);
        qrCodeFoundButton = findViewById(R.id.cameraActivity_qrCodeFoundButton);

        qrCodeFoundButton.setVisibility(View.INVISIBLE);
        qrCodeFoundButton.setOnClickListener(new View.OnClickListener() {
            // onClick function for the QR Code Found Button
            @Override
            public void onClick(View view){
                Log.i(qrScanCameraActivity.class.getSimpleName(), "QR Code Found: " + qrCode);
                Intent qrScannedIntent = new Intent(qrScanCameraActivity.this, qrScannedGetInfoActivity.class);
                qrScannedIntent.putExtra("QR_Content", qrCode);
                qrScannedIntent.putExtra("entryFromScanBool", true);
                startActivity(qrScannedIntent);
            }
        });

        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        requestCamera();
    }

    /**
     * Requests permission to use the camera in the application
     */
    private void requestCamera() {
        if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
            startCamera();
        }else{
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)){
                ActivityCompat.requestPermissions(qrScanCameraActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            }else{
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            }
        }
    }

    @SuppressLint("MissingSuperCall") //Do not need the super method call unless dealing with fragments
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
        if(requestCode == PERMISSION_REQUEST_CAMERA){
            if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //if permissions were granted start camera
                startCamera();
            }else{
                Toast.makeText(this, "Camera Permission Denied, please allow camera use in settings", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**
     * start the camera and bind the preview correctly
     * catch any exceptions
     */
    private void startCamera(){
        cameraProviderFuture.addListener(() -> {
            try{
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindCameraPreview(cameraProvider);
            }catch(ExecutionException | InterruptedException e) { //catch any errors when starting the camera
                Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    /**
     * binds the created camera to the camera preview view in order to allow user to see camera view
     * set the analyzer for the camera to detect QR Codes
     * @param cameraProvider object representing the Camera
     */
    private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider){
        cameraPreviewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);

        Preview preview = new Preview.Builder().build();

        CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();

        preview.setSurfaceProvider(cameraPreviewView.getSurfaceProvider());

        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(new Size(1280,720))
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();

        imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeListener() {
            @Override
            public void qrCodeFound(String _qrCode) {
                qrCode = _qrCode;
                qrCodeFoundButton.setVisibility(View.VISIBLE);
            }

            @Override
            public void qrCodeNotFound() {
                qrCodeFoundButton.setVisibility(View.INVISIBLE);
            }
        }));

        Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
    }

    @Override
    public void onBackPressed() {
        finish();
    }

}

I'm not sure where else the issue could exist, because all of the code work perfectly. My phone is operating on Android 11, API 30, and my emulator is running on the same thing (Pixel XL, API 30, Android 11)
Any help with this would be greatly appreciated

Edit: Added analyzer code

/**
 * Class for analyzing frames of camera to check for QR Code, and decode QR Code
 * Code is referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
 */
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
    private QRCodeListener listener;

    /**
     * Constructor for QRCodeImageAnalyzer
     * @param listener
     */
    public QRCodeImageAnalyzer(QRCodeListener listener){
        this.listener = listener;
    }

    @Override
    public void analyze(@NonNull ImageProxy image){
        if(image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888){
            ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
            byte[] imageData = new byte[byteBuffer.capacity()];
            byteBuffer.get(imageData);

            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, image.getWidth(), image.getHeight(),
                    0, 0, image.getWidth(), image.getHeight(), false);

            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

            try{
                Result result = new QRCodeMultiReader().decode(binaryBitmap);
                listener.qrCodeFound(result.getText());
            }catch(FormatException | ChecksumException | NotFoundException e) {
                listener.qrCodeNotFound();
            }
        }
        image.close();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

悲喜皆因你 2025-01-23 16:18:35

您没有提供 QRCodeImageAnalyzer 实现,也许问题就在那里。

以下代码对我有用。注意:我在调用活动而不是扫描仪活动中进行相机权限检查,因此这里缺少它。

public final class ScanQRCodeActivity extends AppCompatActivity {

    public static final String EXTRA_SCANNED_QR_CODE_TEXT = "mypackage.QR_CODE_TEXT";

    private final ExecutorService cameraExecutor = Executors.newSingleThreadExecutor();
    private ProcessCameraProvider cameraProvider;
    private Camera camera;

    private ActivityScanQrCodeBinding binding;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityScanQrCodeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        startCamera();
    }

    @Override
    protected void onDestroy() {
        cameraExecutor.shutdown();
        super.onDestroy();
    }

    private void startCamera() {
        Executor mainExecutor = ContextCompat.getMainExecutor(this);
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(() -> onGotCamera(cameraProviderFuture), mainExecutor);
    }

    @MainThread
    private void onGotCamera(@NonNull ListenableFuture<ProcessCameraProvider> cameraProviderFuture) {
        Preview previewUseCase = new Preview.Builder().build();
        previewUseCase.setSurfaceProvider(binding.scanQrCodePreviewView.getSurfaceProvider());

        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build();

        BarcodeAnalyzer barcodeAnalyzer = new BarcodeAnalyzer(this::onGotBarcodes);
        imageAnalysis.setAnalyzer(cameraExecutor, barcodeAnalyzer);

        try {
            cameraProvider = cameraProviderFuture.get();
            cameraProvider.unbindAll();
            CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
            camera = cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase, imageAnalysis);
            setupTapToFocus();
        }
        catch (ExecutionException | InterruptedException e) {
            setResultCanceledAndFinish();
        }
    }

    @MainThread
    private void onGotBarcodes(@NonNull List<Barcode> barcodes) {
        cameraProvider.unbindAll();     // we don't want any more barcodes
        String scannedQRCodeText = barcodes.get(0).getDisplayValue();
        setResultOkAndFinish(scannedQRCodeText);
    }

    private void setResultOkAndFinish(@Nullable String scannedQRCodeText) {
        setResult(RESULT_OK, new Intent().putExtra(EXTRA_SCANNED_QR_CODE_TEXT, scannedQRCodeText));
        finish();
    }

    private void setResultCanceledAndFinish() {
        setResult(RESULT_CANCELED);
        finish();
    }

    private void setupTapToFocus() {
        binding.scanQrCodePreviewView.setOnTouchListener(this::onTouch);
    }

    private boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
        int actionMasked = event.getActionMasked();

        if (actionMasked == MotionEvent.ACTION_UP) {
            view.performClick();
            return false;
        }

        if (actionMasked != MotionEvent.ACTION_DOWN) {
            return false;
        }

        focus(event.getX(), event.getY());
        return true;
    }

    private void focus(float focusPointX, float focusPointY) {
        CameraControl cameraControl = camera.getCameraControl();

        MeteringPointFactory factory = binding.scanQrCodePreviewView.getMeteringPointFactory();
        MeteringPoint point = factory.createPoint(focusPointX, focusPointY);
        FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
                .addPoint(point, FocusMeteringAction.FLAG_AE)
                .addPoint(point, FocusMeteringAction.FLAG_AWB)
                .build();

        cameraControl.startFocusAndMetering(action);
    }
}

分析器:

public final class BarcodeAnalyzer implements ImageAnalysis.Analyzer {

    private final BarcodeScanner barcodeScanner = createBarcodeScanner();
    private final BarcodesListener barcodesListener;

    @FunctionalInterface
    public interface BarcodesListener {
        void invoke(List<Barcode> barcodes);
    }

    public BarcodeAnalyzer(BarcodesListener barcodesListener) {
        this.barcodesListener = barcodesListener;
    }

    @ExperimentalGetImage
    @Override
    public void analyze(@NonNull ImageProxy imageProxy) {
        Image image = imageProxy.getImage();
        if (image == null) {
            imageProxy.close();
            return;
        }

        int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
        InputImage inputImage = InputImage.fromMediaImage(image, rotationDegrees);
        barcodeScanner.process(inputImage)
                .addOnSuccessListener(this::onGotBarcodes)
                .addOnCompleteListener(task -> imageProxy.close());
    }

    private void onGotBarcodes(List<Barcode> barcodes) {
        if (barcodes != null && !barcodes.isEmpty()) {
            barcodesListener.invoke(barcodes);
        }
    }

    private BarcodeScanner createBarcodeScanner() {
        BarcodeScannerOptions options = new BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build();
        return BarcodeScanning.getClient(options);
    }
}

所需的依赖项(camerax 和 MLKit):

dependencies {
    // ...
    
    implementation 'androidx.camera:camera-camera2:1.1.0-beta02'
    implementation 'androidx.camera:camera-lifecycle:1.1.0-beta02'
    implementation 'androidx.camera:camera-view:1.1.0-beta02'
    implementation 'com.google.mlkit:barcode-scanning:17.0.2'

You didn't provide your QRCodeImageAnalyzer implementation, maybe the issue lies there.

Following code works for me. Note: I do the camera permission check in the calling activity not the scanner activity so it's missing here.

public final class ScanQRCodeActivity extends AppCompatActivity {

    public static final String EXTRA_SCANNED_QR_CODE_TEXT = "mypackage.QR_CODE_TEXT";

    private final ExecutorService cameraExecutor = Executors.newSingleThreadExecutor();
    private ProcessCameraProvider cameraProvider;
    private Camera camera;

    private ActivityScanQrCodeBinding binding;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityScanQrCodeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        startCamera();
    }

    @Override
    protected void onDestroy() {
        cameraExecutor.shutdown();
        super.onDestroy();
    }

    private void startCamera() {
        Executor mainExecutor = ContextCompat.getMainExecutor(this);
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(() -> onGotCamera(cameraProviderFuture), mainExecutor);
    }

    @MainThread
    private void onGotCamera(@NonNull ListenableFuture<ProcessCameraProvider> cameraProviderFuture) {
        Preview previewUseCase = new Preview.Builder().build();
        previewUseCase.setSurfaceProvider(binding.scanQrCodePreviewView.getSurfaceProvider());

        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build();

        BarcodeAnalyzer barcodeAnalyzer = new BarcodeAnalyzer(this::onGotBarcodes);
        imageAnalysis.setAnalyzer(cameraExecutor, barcodeAnalyzer);

        try {
            cameraProvider = cameraProviderFuture.get();
            cameraProvider.unbindAll();
            CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
            camera = cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase, imageAnalysis);
            setupTapToFocus();
        }
        catch (ExecutionException | InterruptedException e) {
            setResultCanceledAndFinish();
        }
    }

    @MainThread
    private void onGotBarcodes(@NonNull List<Barcode> barcodes) {
        cameraProvider.unbindAll();     // we don't want any more barcodes
        String scannedQRCodeText = barcodes.get(0).getDisplayValue();
        setResultOkAndFinish(scannedQRCodeText);
    }

    private void setResultOkAndFinish(@Nullable String scannedQRCodeText) {
        setResult(RESULT_OK, new Intent().putExtra(EXTRA_SCANNED_QR_CODE_TEXT, scannedQRCodeText));
        finish();
    }

    private void setResultCanceledAndFinish() {
        setResult(RESULT_CANCELED);
        finish();
    }

    private void setupTapToFocus() {
        binding.scanQrCodePreviewView.setOnTouchListener(this::onTouch);
    }

    private boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
        int actionMasked = event.getActionMasked();

        if (actionMasked == MotionEvent.ACTION_UP) {
            view.performClick();
            return false;
        }

        if (actionMasked != MotionEvent.ACTION_DOWN) {
            return false;
        }

        focus(event.getX(), event.getY());
        return true;
    }

    private void focus(float focusPointX, float focusPointY) {
        CameraControl cameraControl = camera.getCameraControl();

        MeteringPointFactory factory = binding.scanQrCodePreviewView.getMeteringPointFactory();
        MeteringPoint point = factory.createPoint(focusPointX, focusPointY);
        FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
                .addPoint(point, FocusMeteringAction.FLAG_AE)
                .addPoint(point, FocusMeteringAction.FLAG_AWB)
                .build();

        cameraControl.startFocusAndMetering(action);
    }
}

The analyzer:

public final class BarcodeAnalyzer implements ImageAnalysis.Analyzer {

    private final BarcodeScanner barcodeScanner = createBarcodeScanner();
    private final BarcodesListener barcodesListener;

    @FunctionalInterface
    public interface BarcodesListener {
        void invoke(List<Barcode> barcodes);
    }

    public BarcodeAnalyzer(BarcodesListener barcodesListener) {
        this.barcodesListener = barcodesListener;
    }

    @ExperimentalGetImage
    @Override
    public void analyze(@NonNull ImageProxy imageProxy) {
        Image image = imageProxy.getImage();
        if (image == null) {
            imageProxy.close();
            return;
        }

        int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
        InputImage inputImage = InputImage.fromMediaImage(image, rotationDegrees);
        barcodeScanner.process(inputImage)
                .addOnSuccessListener(this::onGotBarcodes)
                .addOnCompleteListener(task -> imageProxy.close());
    }

    private void onGotBarcodes(List<Barcode> barcodes) {
        if (barcodes != null && !barcodes.isEmpty()) {
            barcodesListener.invoke(barcodes);
        }
    }

    private BarcodeScanner createBarcodeScanner() {
        BarcodeScannerOptions options = new BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build();
        return BarcodeScanning.getClient(options);
    }
}

Required dependencies (camerax an MLKit):

dependencies {
    // ...
    
    implementation 'androidx.camera:camera-camera2:1.1.0-beta02'
    implementation 'androidx.camera:camera-lifecycle:1.1.0-beta02'
    implementation 'androidx.camera:camera-view:1.1.0-beta02'
    implementation 'com.google.mlkit:barcode-scanning:17.0.2'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文