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

Android Camera API调用startPreview失败运行时异常求助

解决Android Camera API多次拍摄后startPreview失败的问题

这种多次拍摄后触发startPreview failed的RuntimeException我之前做相机应用时也踩过坑,大概率是相机状态混乱或者资源没处理到位导致的,咱们一步步来排查解决:

核心问题分析

你当前的逻辑是拍完照直接在onPictureTaken里调用startPreview(),但相机刚完成拍照操作,内部状态还没完全回到可预览的就绪状态,多次重复这个操作后就容易出现状态冲突,引发崩溃。

具体解决方案

1. 给相机状态恢复留缓冲时间,先停再启预览

在onPictureTaken回调里,先停止当前预览,处理完照片数据后延迟一小段时间再重启预览,给相机足够的状态重置时间:

@Override
public void onPictureTaken(final byte[] data, Camera camera) {
    // 先停止当前预览,避免状态冲突
    camera.stopPreview();
    
    // 这里处理照片数据(比如写入文件、压缩等)
    savePictureData(data);
    
    // 延迟100-300ms再启动预览,时间可根据设备调整
    new Handler(Looper.getMainLooper()).postDelayed(() -> {
        try {
            camera.startPreview();
        } catch (RuntimeException e) {
            // 捕获异常,触发相机重置逻辑
            resetCamera();
        }
    }, 200);
}

2. 确保相机资源被正确释放,避免泄漏

多次拍摄后如果资源没妥善回收,会导致相机服务异常。一定要在Activity的生命周期方法里释放相机:

@Override
protected void onPause() {
    super.onPause();
    releaseCamera();
}

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

private void releaseCamera() {
    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
}

同时,当startPreview失败时,要做相机重置逻辑,彻底释放后重新初始化:

private void resetCamera() {
    releaseCamera();
    // 重新初始化相机
    initCamera();
}

private void initCamera() {
    try {
        mCamera = Camera.open();
        // 配置相机参数(预览尺寸、拍照尺寸等)
        setupCameraParams();
        mCamera.setPreviewDisplay(mSurfaceHolder);
        mCamera.startPreview();
    } catch (IOException | RuntimeException e) {
        e.printStackTrace();
    }
}

3. 避免相机参数冲突,确保参数配置正确

多次拍摄后可能出现参数被意外修改的情况,每次启动预览前重新确认相机参数:

private void setupCameraParams() {
    if (mCamera == null) return;
    Camera.Parameters params = mCamera.getParameters();
    
    // 选择适配SurfaceView的最优预览尺寸
    List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
    Camera.Size optimalPreviewSize = getOptimalSize(previewSizes, mSurfaceView.getWidth(), mSurfaceView.getHeight());
    params.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);
    
    // 配置拍照尺寸(建议和预览尺寸比例一致,避免拉伸)
    List<Camera.Size> pictureSizes = params.getSupportedPictureSizes();
    Camera.Size optimalPictureSize = getOptimalSize(pictureSizes, optimalPreviewSize.width, optimalPreviewSize.height);
    params.setPictureSize(optimalPictureSize.width, optimalPictureSize.height);
    
    mCamera.setParameters(params);
}

// 自定义方法:获取适配目标宽高的最优尺寸
private Camera.Size getOptimalSize(List<Camera.Size> sizes, int targetWidth, int targetHeight) {
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) targetWidth / targetHeight;
    Camera.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    for (Camera.Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // 如果没找到比例匹配的,选高度最接近的
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }
    return optimalSize;
}

额外提示

  • 确保SurfaceView的Surface已经创建完成后再初始化相机,可以在SurfaceHolder.Callback的surfaceCreated或surfaceChanged方法中调用initCamera()。
  • 不要忽略startPreview抛出的异常,捕获后做优雅降级(比如提示用户重启相机),提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:11:34