29、兼容性适配:碎片化问题、编解码器兼容、权限适配、Android版本适配
做音视频开发,最头疼的是什么?
不是算法难,不是API复杂。而是——同一套代码,在小米上跑得飞起,在华为上黑屏,在OPPO上没声音,在老的三星上直接闪退。
这就是Android碎片化。我做了这么多年音视频,踩过的坑能写满一本笔记本。今天咱们就聊聊,怎么跟这些碎片化问题和平共处。
29.1 碎片化问题的本质
Android碎片化体现在三个维度:
- 硬件碎片化:芯片不同(高通、联发科、麒麟、Exynos),GPU不同,摄像头传感器不同
- 系统碎片化:从Android 5.0到Android 14,每个版本API差异巨大
- 厂商定制化:MIUI、ColorOS、EMUI、One UI……每家都改底层
说白了,你面对的不是一个Android,而是几百个Android。
核心原则:不要假设任何设备的行为是"标准的"。永远做防御性编程。
29.2 编解码器兼容:最痛的坑
编解码器兼容,是我个人认为音视频适配里最麻烦的部分。为什么?因为MediaCodec的行为,在不同芯片上完全不一样。
29.2.1 支持的编码格式差异
你以为所有设备都支持H.264硬编?太天真了。
| 编码格式 | 高通 | 联发科 | 麒麟 | Exynos |
|---|---|---|---|---|
| H.264 | ✅ 全支持 | ✅ 全支持 | ✅ 全支持 | ✅ 全支持 |
| H.265 | ✅ 大部分 | ⚠️ 部分低端不支持 | ✅ 支持 | ✅ 支持 |
| VP8 | ✅ 支持 | ⚠️ 部分不支持 | ❌ 不支持 | ✅ 支持 |
| AV1 | ❌ 需软解 | ❌ 需软解 | ❌ 需软解 | ❌ 需软解 |
我在项目中遇到过,某款联发科低端平板,硬编H.265出来的视频全是花屏。查了两天才发现,它的H.265编码器有bug,只能输出I帧,P帧全丢了。
怎么解决?我的习惯是:
- 先查MediaCodecList,确认设备支持哪些编码器
- 准备一个降级策略:硬编不行就软编,H.265不行就降级到H.264
- 关键场景做编码器自测:编一个短片段,解码验证质量
// 查询设备支持的编码器
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
String[] supportedTypes = codecList.getSupportedTypes();
// 检查特定编码器是否支持
boolean isH264Supported = false;
for (String type : supportedTypes) {
if (type.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
isH264Supported = true;
break;
}
}
// 降级策略示例
if (!isH264Supported) {
// 降级到软件编码
useSoftwareEncoder();
}
29.2.2 解码器兼容问题
解码器的问题更隐蔽。我记得有一次,用户反馈说播放某个MP4文件,在小米10上正常,在红米Note 9上只有声音没有画面。
查了半天,原因是那个视频的H.264编码使用了High Profile的某些特性,红米的解码器不支持。嗯,这里要注意:
- 不同厂商的解码器能力边界不一样
- 有些解码器对B帧数量有限制
- 参考帧数量超过4个,部分老解码器直接罢工
我的建议:编码时尽量用Baseline Profile或Main Profile,别用High Profile的奇奇怪怪特性。兼容性比画质重要。
29.3 权限适配:从M到14的进化史
权限问题,说白了就是Google一直在收紧,厂商一直在加戏。
29.3.1 运行时权限
Android 6.0(API 23)引入了运行时权限。如果你还在用targetSdkVersion 22以下,那当我没说。但新应用必须适配。
音视频开发涉及的权限:
- CAMERA:摄像头采集
- RECORD_AUDIO:麦克风录音
- READ_EXTERNAL_STORAGE:读取媒体文件
- WRITE_EXTERNAL_STORAGE:保存录制文件
// 请求权限的标准做法
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
29.3.2 Android 10+ 的存储限制
Android 10(API 29)开始,强制推行分区存储(Scoped Storage)。你不能再随便往/sdcard/里写文件了。
我曾经有个项目,升级targetSdkVersion到29后,录制的视频全保存失败。查了半天才发现,MediaStore的URI用错了。
避坑指南:Android 10以上,保存媒体文件要用MediaStore,别再用File API直接写路径了。Android 11更严格,连读取都需要MANAGE_EXTERNAL_STORAGE权限。
// Android 10+ 保存视频的正确方式
ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DISPLAY_NAME, "video_" + System.currentTimeMillis() + ".mp4");
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);
Uri uri = getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
// 通过uri写入数据
29.3.3 Android 12+ 的蓝牙权限
Android 12(API 31)把蓝牙权限拆成了BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE。如果你的应用需要连接蓝牙耳机或蓝牙麦克风,记得适配。
29.4 Android版本适配要点
不同Android版本,API行为差异很大。我整理了一张表,方便你对照:
| Android版本 | API Level | 音视频相关变化 | 适配要点 |
|---|---|---|---|
| 6.0 | 23 | 运行时权限 | 必须动态申请权限 |
| 7.0 | 24 | FileProvider | 相机拍照用FileProvider |
| 8.0 | 26 | 后台执行限制 | 后台录音需前台服务 |
| 9.0 | 28 | 非SDK接口限制 | 别用隐藏API |
| 10 | 29 | 分区存储 | 用MediaStore |
| 11 | 30 | 包可见性 | 查询其他应用需 |
| 12 | 31 | 蓝牙权限拆分 | 分场景申请 |
| 13 | 33 | 通知权限 | 需POST_NOTIFICATIONS |
| 14 | 34 | 前台服务类型 | 声明foregroundServiceType |
29.5 知识体系总览
下面这张图,是我对兼容性适配的整体理解。你想想看,适配工作其实就围绕这三个维度展开:
29.6 实战建议
说了这么多,最后给几条实在的建议:
- 测试设备要覆盖主流芯片:高通、联发科、麒麟各来一台。别只测旗舰机,中低端机才是用户量最大的。
- 建立兼容性清单:每发现一个兼容性问题,就记录下来。我自己的清单已经积累了200多条。
- 灰度发布:新功能先放5%的用户,观察崩溃率和兼容性反馈,没问题再全量。
- 云端降级:如果某个设备编解码有问题,服务端可以下发配置,强制走软解或降码率。
最后说一句:兼容性适配没有银弹。你只能靠经验积累和充分测试。但只要你把上面这些点都考虑到了,至少能覆盖90%的问题。
公众号:蓝海资料掘金营,微信deep3321