第3章:常见权限不足症状速查表

权限不足问题在 ExoPlayer 集成中极为隐蔽——播放器可能只抛出一个通用异常,甚至静默失败。本章建立“症状 → 权限映射表”,并解析典型用户反馈,帮助您从现象快速定位缺失权限。

3.1 现象识别:症状与权限对应矩阵

下表汇总了最常见的播放失败症状及其指向的 Android 权限,以及典型日志关键词。当您遇到任一症状时,优先对照此表。

症状(用户可见) 最可能缺失的权限 典型日志/异常关键词 影响场景
点击播放后立即静默停止,无错误提示 INTERNET Network request failed、java.net.UnknownHostException URL 流媒体 / Dash / HLS
黑屏但有声音,或画面卡在第一帧 INTERNET(网络超时)或 READ_EXTERNAL_STORAGE(本地视频) DrmSessionException、MediaSourceException DRM 内容 / 本地高清文件
提示“无法打开文件”或“文件不存在” READ_EXTERNAL_STORAGE(Android 12 及以下)/READ_MEDIA_VIDEO(Android 13+) java.io.FileNotFoundException: Permission denied 本地 MP4 / MKV 文件
播放器初始化成功,但进度条不动,无缓冲事件 INTERNET(断网)或 ACCESS_NETWORK_STATE(无法检测网络变化) WARN ExoPlayerImpl: No internet connectivity 在线直播 / 自适应码率
音频正常播放,视频画面全黑或绿色 READ_EXTERNAL_STORAGE(存储路径错误)或 INTERNET(视频流损坏) MediaCodecException: Codec not found(不常见权限外因素) 本地文件或加密流
点击播放按钮后无任何响应,Logcat 无输出 权限被系统完全静默拒绝(如 INTERNET 或存储权限) E/ActivityThread: Failed to find provider info(少数情况) 所有流媒体
⚠️ 权限作用范围注意
INTERNET 权限在 Android 6.0+ 仍属于普通权限(无需运行时申请),但若被用户或 adb 强制撤销,播放器会静默失败。存储权限在 Android 13 上被拆分为更细粒度的 READ_MEDIA_VIDEO 和 READ_MEDIA_AUDIO,切勿遗漏。

3.2 典型用户反馈解析

将模糊的用户报告转化为权限排查线索,是问题排查的核心能力。以下三条真实反馈经脱敏处理,附解析步骤。

反馈 A:“我用 ExoPlayer 播放手机录制的一个 4K 视频,打开 App 后黑屏,但能听到系统提示音。”
  1. 现象定性:有声音无画面 → 视频解码器问题或权限获取失败导致 Surface 未准备。
  2. 排查方向:确认是否因存储权限被拒,导致 ExoPlayer 无法读取视频文件头信息(仅读取元数据也需要读权限)。
  3. 实际根因:Android 11 及以上 Scoped Storage 下,App 未声明 READ_EXTERNAL_STORAGE,且未使用 SAF 选择文件。
  4. 修复:在 AndroidManifest.xml 添加 READ_EXTERNAL_STORAGE(Android 12 及以下)或 READ_MEDIA_VIDEO(Android 13+),并动态请求。
反馈 B:“播放 m3u8 直播流时,前 10 秒正常,然后卡住不动,点击暂停/继续无反应。”
  1. 现象定性:初始加载成功,后续断流 → 网络中断或带宽不足;权限问题通常表现为一上来就失败。
  2. 排查修正:检查接口返回 403 或 SSL 握手失败(需 INTERNET 权限),但本案例 Logcat 显示 WARN ExoPlayerImpl: No internet connectivity。
  3. 根因:ACCESS_NETWORK_STATE 权限未申请,导致系统网络监听回调未触发,ExoPlayer 内部网络状态机卡在“待机”模式。
  4. 修复:在 Manifest 中添加 ACCESS_NETWORK_STATE,并在 onStart() 中调用 ConnectivityManager.registerDefaultNetworkCallback 刷新状态。
反馈 C:“同一套代码在旧手机(Android 9)正常,新手机(Android 14)直接闪退,无任何错误弹窗。”
  1. 现象定性:版本兼容性崩溃 → 大概率是新版本权限模型变化导致的启动崩溃。
  2. 排查方向:在 AndroidManifest.xml 中寻找是否包含 android:maxSdkVersion="31" 限制旧权限的声明。若 READ_EXTERNAL_STORAGE 被限制在 maxSdkVersion=32,Android 14 上该权限不被识别,会导致 RuntimeException: Missing permission(某些系统版本)。
  3. 实际根因:App 同时声明了 WRITE_EXTERNAL_STORAGE(已废弃)和 READ_EXTERNAL_STORAGE,在 Android 14 上被拒绝访问,播放器初始化时尝试写入临时缓存文件失败。
  4. 修复:移除 WRITE_EXTERNAL_STORAGE 声明(播放器不需要),并针对 Android 13+ 单独声明媒体分组权限。

3.3 故障树:播放失败 → 权限缺失定位

以下故障树可以帮助您从最终用户看到的症状,逐层推理到特定权限问题。图中使用标准颜色标识:🔴 红色节点为终端症状,🟡 黄色为中间判据,🟢 绿色为排查结论。

播放失败权限缺失故障树(ExoPlayer) 播放失败 黑屏/有声音无画面 文件无法打开/错误 网络播放卡住/断流 检查文件读取权限 检测 DRM 与网络权限 检查 Manifest 权限声明 动态申请存储权限 检查 INTERNET 权限 检查 NETWORK_STATE 结论:补充 READ_EXTERNAL_STORAGE / READ_MEDIA_VIDEO 结论:增加动态权限请求逻辑 结论:添加 NETWORK_STATE 声明 提示:每个分支均需先确认 Android API Level,再选择对应权限名称。 Android 13+ 需使用 READ_MEDIA_VIDEO 而非 READ_EXTERNAL_STORAGE。

3.4 数据采集:日志与调试命令

当速查表无法明确匹配时,通过以下命令采集现场证据:

  1. 采集 App 启动日志(过滤 ExoPlayer 和权限相关):
    adb logcat -b main -b events -v time | grep -E "(ExoPlayer|Permission|SecurityException|MediaCodec)"
  2. 检查 Manifest 中声明的权限列表:
    adb shell dumpsys package your.package.name | grep permission
  3. 强制触发权限拒绝以复现行为(需 root 或测试设备):
    adb shell pm revoke your.package.name android.permission.INTERNET
💡 实战技巧
在 ExoPlayer.Builder 设置 setLogTAG("ExoPlayerTag") 自定义 TAG,方便过滤;此外,在 Player.Listener.onPlayerError 回调中打印 error.message 和 error.cause,可直接看到底层异常链。

3.5 修复动作与复测

根据定位结果执行对应修复,然后使用以下脚本快速复测:

// Kotlin 示例:动态请求存储权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    ActivityCompat.requestPermissions(activity,
        arrayOf(Manifest.permission.READ_MEDIA_VIDEO), REQUEST_CODE)
} else {
    ActivityCompat.requestPermissions(activity,
        arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE)
}

复测步骤:

  1. 清除 App 数据或卸载重装,重置权限状态。
  2. 首次启动时拒绝权限请求,确认播放器能优雅降级(显示提示而非崩溃)。
  3. 授予权限后重新播放,观察是否恢复到正常画面。
  4. 使用 adb shell dumpsys 验证权限已成功授予。

3.6 预防措施清单

  • 统一权限管理类:封装 PermissionManager 代理所有运行时请求,避免每个 Activity 重复代码。
  • Manifest 声明全量覆盖:同时声明旧版和新版权限(使用 maxSdkVersion 限制旧权限范围)。
  • 预处理检测:在播放前调用 ContextCompat.checkSelfPermission,若未授权则弹出引导对话框,而非直接崩溃。
  • 集成自动化测试:使用 Espresso 或 UI Automator 模拟权限授予/拒绝场景,验证播放器状态机。
本章小结:通过症状速查表快速映射权限缺失类型;利用故障树从用户反馈归纳排查路径;始终在 Android 13+ 设备上使用 READ_MEDIA_VIDEO 代替旧存储权限。