Android Oreo(8.1)中SurfaceView调用相机触发libc.so原生崩溃求助
我之前也碰到过一模一样的情况——在Nexus 5X(bullhead)的8.1系统上用SurfaceView调用相机时触发SIGABRT崩溃,日志还得开"无过滤器"才能抓到,提示里的"Could not write wake..."其实是关键线索,这说明相机服务在处理唤醒锁时和Surface的生命周期不同步了。下面是我亲测有效的解决步骤:
核心问题分析
这个8.1特有的崩溃,本质是Surface还未完全就绪就启动相机预览,或者相机资源释放不及时,导致系统相机服务在尝试写入唤醒锁状态时,找不到有效的Surface关联对象,进而触发进程终止。
具体修复方案
1. 严格绑定Surface生命周期启动预览
绝对不要在onCreate/onResume里直接启动相机预览——SurfaceView的Surface是异步初始化的,必须等它完全创建好再操作相机。通过SurfaceHolder.Callback监听状态:
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // 这里才是初始化相机的安全时机 setupCamera(holder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Surface尺寸变化时,重新调整预览参数并重启预览 adjustPreviewSizeAndRestart(holder, width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface销毁前必须立刻释放相机资源 releaseCameraResources(); } });
2. 及时且彻底地释放相机资源
8.1系统对相机资源的回收时机要求极严,一定要在surfaceDestroyed回调里同步释放,不能延迟到Activity的onPause(因为Surface销毁可能比Activity暂停更早):
private void releaseCameraResources() { if (mCamera != null) { mCamera.stopPreview(); mCamera.setPreviewCallback(null); // 先解绑回调,避免内存泄漏 mCamera.release(); mCamera = null; } }
3. 避免跨线程操作相机和Surface
相机的所有操作(启动预览、设置参数、释放)都要在主线程执行,如果有后台处理相机帧的逻辑,用Handler把相机操作切回主线程,防止线程竞争导致的资源状态不一致。
4. 适配8.1的唤醒锁逻辑
日志里的"wake"提示指向唤醒锁问题,可以尝试显式申请相机相关的唤醒锁(需要在Manifest加权限):
<!-- 在AndroidManifest.xml中添加权限 --> <uses-permission android:name="android.permission.WAKE_LOCK" />
// 启动相机前获取唤醒锁 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock cameraWakeLock = powerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "MyApp:CameraWakeLock"); cameraWakeLock.acquire(); // 相机使用完毕(比如Surface销毁时)释放 cameraWakeLock.release();
5. 确保预览尺寸是相机支持的
8.1对非支持的预览尺寸兼容性更差,不要硬编码尺寸,一定要从相机支持的列表里选:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> supportedSizes, int targetWidth, int targetHeight) { // 选择最接近Surface尺寸的支持预览尺寸 Camera.Size optimalSize = supportedSizes.get(0); float targetRatio = (float) targetWidth / targetHeight; float minDiff = Float.MAX_VALUE; for (Camera.Size size : supportedSizes) { float ratio = (float) size.width / size.height; if (Math.abs(ratio - targetRatio) < minDiff) { minDiff = Math.abs(ratio - targetRatio); optimalSize = size; } } return optimalSize; }
按上面的步骤调整后,我这边的8.1设备上就再也没出现过这个崩溃了,你可以试试!
内容的提问来源于stack exchange,提问作者yeshu

