You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android二次启动扫码Activity时条码扫描功能失效问题

Android CameraX 二次启动扫码页无法识别QR码问题

问题现象

  • 基于CameraX实现安卓应用QR扫码功能,项目分为主Activity、扫码Activity两个页面:主Activity负责启动扫码页,扫码页检测到符合自定义规则(匹配指定scheme、host、path)的URI时,自动销毁页面返回主Activity,弹出对应业务对话框。
  • 功能首次运行完全正常,但第二次及后续启动扫码Activity时,相机预览画面正常显示,但始终无法识别QR码,onQRCodeFound回调不会被触发;杀死应用重启后可恢复正常,仅能完成1次扫码,再次打开扫码页问题复现。
  • 问题复现时,日志持续打印错误:W/System.err: com.google.zxing.NotFoundException

排查进展

  • 定位到直接诱因:二次及后续启动扫码Activity时,previewView控件的宽高取值均为0(首次启动时可正常获取1080x2280的实际尺寸),为ImageAnalysis分析用例设置0x0的无效目标分辨率,直接导致条码识别失效。
  • 验证可行的临时方案1:放弃动态获取previewView宽高的逻辑,直接硬编码分辨率数值,每次启动扫码功能均可正常运行。
  • 验证可行的临时方案2:将bindCameraPreview(cameraProvider)的调用修改为previewView.post(()->bindCameraPreview(cameraProvider)),通过post延迟绑定操作,功能也可正常运行。
  • 目前不确定post延迟绑定的方案是否为最优/标准实现,仅确认可解决当前问题;尚未明确根因:为何previewView首次启动时可正常获取尺寸,二次启动时宽高取值为0,欢迎知晓原因的开发者解答。

相关实现代码

ScanQRActivity 代码

public class ScanQRActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CAMERA = 0;
    private PreviewView previewView;
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
    private Button qrCodeFoundButton;
    private String qrCode;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qr_scan);

        previewView = findViewById(R.id.activity_main_previewView);

        qrCodeFoundButton = findViewById(R.id.activity_main_qrCodeFoundButton);
        qrCodeFoundButton.setVisibility(View.INVISIBLE);

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

    private void requestCamera() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            startCamera();
        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
                ActivityCompat.requestPermissions(ScanQRActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            } else {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startCamera();
            } else {
                // Toast.makeText(this, "Camera Permission Denied", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void startCamera() {
        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindCameraPreview(cameraProvider);


            } catch (ExecutionException | InterruptedException e) {
                Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider) {
        previewView.setImplementationMode(PreviewView.ImplementationMode.COMPATIBLE);
        Preview preview = new Preview.Builder()
                .build();

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

        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        ImageAnalysis imageAnalysis =
                new ImageAnalysis.Builder()
                        .setTargetResolution(new Size(previewView.getWidth(), previewView.getHeight() ))
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .build();

        imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeImageAnalyzer.QRCodeFoundListener() {
            @Override
            public void onQRCodeFound(String _qrCode) {
                qrCode = _qrCode;
                qrCodeFoundButton.setVisibility(View.VISIBLE);
                Uri code = Uri.parse(qrCode);
                if(code.getScheme().equals("myCustomScheme") && code.getHost().equals("app")){
                   String path = code.getPath();
                   String host = code.getHost() ;
                  String query =  code.getQuery();
                  List<String> params = code.getQueryParameters("a");
                  if(path.equals("/voucher")){
                      Intent intent = new Intent();
                      intent.putExtra("type", "voucher");
                      intent.putExtra("voucherCode", code.getQueryParameter("c"));
                      intent.putExtra("timestamp", code.getQueryParameter("t"));
                      setResult(Activity.RESULT_OK, intent);
                      finish();
                  }
                }
            }

            @Override
            public void qrCodeNotFound() {
             
            }
        }));
        Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector,imageAnalysis, preview);

    }
}

页面返回处理代码

ActivityResultLauncher<Intent> startQRActivity = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        Intent data = result.getData();
                       String code = data.getStringExtra("voucherCode");
                       String time = data.getStringExtra("timestamp");
                       voucherDetailBusinessDialog dia = new voucherDetailBusinessDialog(getColor(R.color.appThemeColor), getColor(R.color.orange), code);
                       dia.show(getSupportFragmentManager(),null);
                    }
                }
            });

内容的提问来源于stack exchange,提问作者Charlie Walker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 02:36:18