29. Camera兼容性处理:不同厂商适配、Android版本差异、常见问题排查
做Android摄像头开发,最头疼的是什么?
不是API不会用,而是同一套代码,在小米上跑得好好的,换到华为就黑屏了。换到OPPO,直接闪退。我刚开始做这块时,被这种问题折磨得够呛。后来慢慢摸清了门道,今天就把这些经验掰开揉碎讲给你听。
核心观点:Camera兼容性问题的本质,是厂商定制ROM + 硬件差异 + Android版本碎片化三者叠加的结果。没有银弹,但有套路。
29.1 厂商适配:那些年我们踩过的坑
国内厂商对Camera HAL层的修改,可以说是「八仙过海,各显神通」。我整理了一份常见问题清单,你看看是不是很眼熟:
| 厂商 | 常见问题 | 我的解决经验 |
|---|---|---|
| 华为/荣耀 | Camera.open() 返回null,或预览画面旋转90度 | 华为部分机型需要先检查Camera.getNumberOfCameras(),再逐个尝试open |
| 小米 | 预览画面卡顿,或拍照后图片方向不对 | 小米的Camera1 API在部分MIUI版本中,setDisplayOrientation无效,需要手动旋转 |
| OPPO/vivo | 后台Camera被系统回收,或权限弹窗异常 | 这两家对后台Camera限制极严,建议onPause时务必release |
| 三星 | Camera.open() 耗时过长,或预览尺寸不支持 | 三星的Camera HAL层实现比较「标准」,但部分低端机型支持的预览尺寸很少 |
我的小技巧:在应用启动时,先跑一个「兼容性检测」流程。遍历所有Camera ID,尝试open、setParameters、startPreview,如果失败就记录日志并降级处理。这样用户至少能看到错误提示,而不是直接闪退。
29.2 Android版本差异:从Camera1到CameraX的进化
Android版本对Camera的影响,说白了就是API的变迁史。我经历过从Camera1到Camera2,再到CameraX的全过程,每个阶段都有坑。
29.2.1 Camera1 API(API 9-28)
这个API虽然老,但兼容性最好。几乎所有Android 5.0以下的设备都靠它。不过有个致命问题:不支持多摄像头。
我记得有一次,用户反馈说「为什么我的手机有两个摄像头,但App只能用主摄?」嗯,这就是Camera1的局限。它只能操作一个Camera对象,没法同时管理前后摄。
29.2.2 Camera2 API(API 21+)
Camera2是Google力推的,功能强大,但兼容性一言难尽。我遇到过最离谱的情况:某款手机明明支持Camera2,但调用createCaptureSession时直接抛IllegalStateException。
// Camera2兼容性检查代码
private boolean isCamera2Supported() {
try {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
String[] cameraIds = manager.getCameraIdList();
for (String id : cameraIds) {
CameraCharacteristics chars = manager.getCameraCharacteristics(id);
Integer level = chars.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
// 注意:有些厂商返回的是INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
// 但实际上支持部分Camera2功能
if (level != null && level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
Log.w("CameraCompat", "Camera " + id + " is LEGACY mode");
// 这里可以降级到Camera1
}
}
return true;
} catch (Exception e) {
return false;
}
}
注意:不要完全依赖INFO_SUPPORTED_HARDWARE_LEVEL来判断。有些厂商的「LEGACY」模式其实能跑Camera2的大部分功能,只是个别特性不支持。我建议你实际调用一下关键API,看会不会抛异常。
29.2.3 CameraX(API 21+,Jetpack)
CameraX是Google的「救火队员」。它封装了Camera2的复杂性,自动处理了大部分兼容性问题。我个人现在做新项目,首选CameraX。
但CameraX也不是万能的。比如它不支持手动对焦的精细控制,也不支持RAW格式输出。如果你需要这些功能,还是得用Camera2。
29.3 常见问题排查:我的「三板斧」
遇到Camera兼容性问题,别慌。我有一套固定的排查流程,90%的问题都能定位到根因。
- 第一板斧:检查权限
- CAMERA权限是否在Manifest中声明?
- 运行时权限是否已授予?
- 部分厂商(如华为)在Android 10以上,还需要检查「后台弹出界面」权限
- 第二板斧:检查生命周期
- Camera对象是否在onResume中打开,在onPause中释放?
- 有没有在子线程中操作Camera?Camera API不是线程安全的
- SurfaceView/TextureView的surfaceCreated回调是否在Camera.open之后?
- 第三板斧:检查参数
- 预览尺寸是否在getSupportedPreviewSizes列表中?
- 图片格式是否支持?大部分设备只支持NV21和YV12
- 帧率是否在合理范围?不要设置30fps以上,很多设备不支持
我曾经遇到一个案例:某款手机在Camera.open()后,调用setParameters就崩溃。查了半天,发现是预览尺寸设置成了1920x1080,但该手机只支持1280x720。所以,永远不要硬编码预览尺寸,一定要从getSupportedPreviewSizes中选一个。
29.4 知识体系:一张图看懂Camera兼容性
下面这张图,是我自己总结的Camera兼容性处理框架。每次遇到新问题,我都会对照这张图来排查。
29.5 实战:一个通用的Camera兼容性工具类
说了这么多理论,来点实际的。下面这个工具类,是我在多个项目中打磨出来的,专门处理Camera兼容性问题。
public class CameraCompatUtils {
/**
* 安全打开Camera,处理各种异常
*/
public static Camera safeOpenCamera(int cameraId) {
try {
return Camera.open(cameraId);
} catch (Exception e) {
// 有些厂商在Camera.open()时可能抛RuntimeException
Log.e("CameraCompat", "open camera failed: " + e.getMessage());
// 尝试降级:用Camera.open()无参版本
try {
return Camera.open();
} catch (Exception e2) {
return null;
}
}
}
/**
* 获取最佳预览尺寸
*/
public static Camera.Size getBestPreviewSize(Camera.Parameters params, int targetWidth, int targetHeight) {
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
if (sizes == null || sizes.isEmpty()) {
return null;
}
// 优先选择与目标尺寸最接近的
Camera.Size bestSize = sizes.get(0);
int bestDiff = Integer.MAX_VALUE;
for (Camera.Size size : sizes) {
int diff = Math.abs(size.width - targetWidth) + Math.abs(size.height - targetHeight);
if (diff < bestDiff) {
bestDiff = diff;
bestSize = size;
}
}
return bestSize;
}
/**
* 安全设置预览方向
*/
public static void safeSetDisplayOrientation(Camera camera, int degrees) {
try {
camera.setDisplayOrientation(degrees);
} catch (Exception e) {
// 部分小米机型会抛异常,忽略即可
Log.w("CameraCompat", "setDisplayOrientation failed: " + e.getMessage());
}
}
}
使用建议:把这个工具类放在你的项目里,所有Camera操作都通过它来执行。这样即使遇到兼容性问题,也能保证App不崩溃,最多是功能降级。
29.6 避坑指南:我的血泪史
最后,分享几个我亲身踩过的坑,希望你别再走一遍。
- 坑1:在Android 6.0以下设备上,Camera.open()可能返回null,但不会抛异常。所以一定要判空。
- 坑2:部分华为手机在调用Camera.setParameters()时,如果参数列表中有不支持的项,会直接抛异常。建议逐个参数设置,并try-catch。
- 坑3:三星手机在横竖屏切换时,Camera对象会被系统重置。记得在onConfigurationChanged中重新初始化Camera。
- 坑4:不要相信getSupportedPictureSizes返回的所有尺寸。有些尺寸虽然列出来了,但实际拍照时会OOM。我建议限制最大图片尺寸不超过1920x1080。
嗯,Camera兼容性处理就是这么回事。说白了,就是「见招拆招」。你遇到的每个问题,大概率别人也遇到过。多查查StackOverflow,多看看厂商的开发者文档,慢慢就能摸清规律了。