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

Android SurfaceView相机打开即闪退、加载慢及相机启动最佳实践咨询

问题解决方法

1. 相机预览立即消失的修复

你遇到的预览消失问题和surfaceDestroyed里的cameraSource.stop()无关,这行是符合生命周期逻辑的正常代码。核心原因是OCR检测器的receiveDetections回调会实时返回识别结果,只要检测到任意文字就会触发resultObtained()方法,方法内执行setContentView(R.layout.activity_main)直接替换了包含SurfaceView的相机预览布局,Surface被销毁后自然会触发停止相机的逻辑,导致预览一闪而过。

修复方案可以二选一:

  • 新增手动识别按钮,删除receiveDetections里自动调用resultObtained()的逻辑,用户点击按钮后再提取识别结果、跳转布局,避免刚打开相机就因为识别到模糊内容自动切页。示例修改代码如下:
// 新增全局按钮变量
private Button captureBtn;

private void textRecognizer(){
    // 原有初始化逻辑保留
    textRecognizer = new TextRecognizer.Builder(getApplicationContext()).build();
    cameraSource = new CameraSource.Builder(getApplicationContext(), textRecognizer)
            .setRequestedPreviewSize(1280, 1024)
            .build();

    surfaceView = findViewById(R.id.surfaceView);
    // 新增按钮初始化
    captureBtn = findViewById(R.id.capture_btn);
    captureBtn.setOnClickListener(v -> {
        if (stringResult != null) {
            resultObtained();
        }
    });

    // 原有Surface回调逻辑保留
    surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
        @SuppressLint("MissingPermission")
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                cameraSource.start(surfaceView.getHolder());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            cameraSource.stop();
        }
    });

    textRecognizer.setProcessor(new Detector.Processor<TextBlock>() {
        @Override
        public void release() {
        }
        @Override
        public void receiveDetections(Detector.Detections<TextBlock> detections) {
            SparseArray<TextBlock> sparseArray = detections.getDetectedItems();
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i<sparseArray.size(); ++i){
                TextBlock textBlock = sparseArray.valueAt(i);
                if (textBlock != null && textBlock.getValue() !=null){
                    stringBuilder.append(textBlock.getValue() + " ");
                }
            }
            stringResult = stringBuilder.toString();
            // 删掉原来自动调用resultObtained的逻辑
        }
    });
}
  • 如果要保留自动识别逻辑,增加识别校验阈值,比如连续3次回调都返回相同文本、或者文本长度满足要求后再触发切页,避免误触发。

2. 相机启动慢优化&最佳实践

现有代码的问题修复

你当前的权限申请代码写法错误,ActivityCompat.requestPermissions的第三个参数是自定义的请求码,你传了PackageManager.PERMISSION_GRANTED会导致权限申请回调异常,这是相机启动慢的核心原因之一,修改为自定义请求码即可:

// 自定义权限请求码
private static final int PERMISSION_REQUEST_CAMERA = 0x101;
// onCreate里的权限申请修改为
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);

同时要重写onRequestPermissionsResult方法,监听权限申请结果,权限通过后再初始化相机相关组件,不要等到点击按钮才初始化。

通用优化方案

  • 不要反复调用setContentView切换布局,每次调用都会重新渲染整个视图树,开销极大。建议将主布局和相机预览布局放在同一个XML文件中,通过View.setVisibility()控制两者的显示隐藏。
  • 不要硬编码预览尺寸,先获取当前设备支持的相机预览分辨率,选择和SurfaceView尺寸最接近的分辨率设置,避免适配开销。
  • 优化生命周期管理:将相机启动逻辑放到onResume中,停止逻辑放到onPause中,减少后台资源占用。
  • 条件允许的情况下,替换老旧的CameraSource为Google官方的CameraX库,CameraX做了大量设备兼容性适配,启动速度更快、适配成本更低,是当前Android相机开发的首选方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:45:07