安卓11+存储权限踩坑实录:BilibiliCacheVideoMerge双模式Path/Uri缓存文件访问方案完整对比
2026/9/19 22:55:19 网站建设 项目流程

安卓11+存储权限踩坑实录:BilibiliCacheVideoMerge双模式Path/Uri缓存文件访问方案完整对比

【免费下载链接】BilibiliCacheVideoMerge🔥🔥Android上将bilibili缓存视频合并导出为mp4,支持安卓5.0 ~ 13,视频挂载弹幕播放(Android consolidates and exports the bilibilibili cache video to mp4, supports Android 5.0~13, and plays the video on the screen)项目地址: https://gitcode.com/gh_mirrors/bi/BilibiliCacheVideoMerge

这篇文章以开源项目 BilibiliCacheVideoMerge(B站缓存视频合并导出 mp4 工具)为例,用一次真实的"踩坑实录"讲清楚:在安卓 11+ 的存储权限新规则下,它如何通过Path 直接路径模式Uri 文件访问模式双方案设计,绕过限制读取 B 站缓存目录。

为什么"读文件"在安卓11上会翻车?

B 站缓存的视频并非一个 mp4,而是一套分片文件,目录结构大致是:

缓存根目录 └── 合集目录(如:某番剧) └── 章节目录(第1集 / 第2集...) ├── audio.m4s ← 音频 ├── video.m4s ← 视频 ├── entry.json ← 元信息(标题、封面、BV号) └── danmaku.xml ← 弹幕

在安卓 10 及以前,应用拿到存储权限后,用File.listFiles()就能直接遍历这套目录。但**安卓 11(API 30)引入范围存储(Scoped Storage)**后,/storage/emulated/0/Android/data/...这类沙盒目录被严格隔离——B 站缓存恰好就住在这里,第三方应用再用传统 File API 直接访问,结果就是:

  • listFiles()返回null或空数组
  • 主页一片空白,缓存列表刷不出来
  • 部分机型直接抛权限异常

这就是本项目要解决的核心矛盾:合并 B 站缓存视频,前提是先"读得到"缓存目录

坑点现场:Path 模式的"失效边界"

Path 模式的实现在 PathCacheFileManager.kt:用File.listFiles()逐层遍历"合集 → 章节",再由 FileTool.getNeedPath() 按文件名识别出audio.m4svideo.m4sentry.jsondanmaku.xml四件套,缺失时通过 needSrcErrorHandle() 给出"xx 下没找到 video.m4s"这类友好提示。

在安卓 11+ 上,这条链路一旦触碰Android/data目录就失效——不是代码写错了,而是系统不再放行。于是作者没有去"硬刚"权限,而是为 Uri 通道做了一套完整平行实现。

双模式架构:Path 与 Uri 全对比

两种模式都实现同一个接口 ICacheFileManager.kt,公共逻辑(全选、刷新、加载弹窗等)收敛在 BaseCacheFileManager.kt 中,是教科书式的策略模式:

对比维度Path 直接模式Uri 文件访问模式
适用场景安卓 10 及以下,或缓存路径不在Android/data安卓 11+ 且路径落在Android/data沙盒内
核心 APIjava.io.File.listFiles()SAF 的DocumentFile+ContentResolver
授权方式传统存储权限SAF 目录选择 + 持久化授权
遍历速度慢(叠加 MMKV 缓存 + 进度弹窗兜底)
核心实现PathCacheFileManager.ktUriCacheFileManager.kt

两条流水线在功能上完全对称:

  • Path 模式:FileTool.getNeedPath() 收集文件的绝对路径
  • Uri 模式:UriTool.getNeedUri() 收集文件的content Uri

每个缓存条目都会通过CacheFile实体中的useUri标记记明"我属于哪种模式",后续合并流程据此走对应分支。

应用如何自动选择模式?

模式不是让用户手动切换的,而是根据路径特征自动判定。在 MainFileShowFragment.java 中:

每次刷新列表前,先用FileTools.underAndroidDataUseUri(path)判断缓存路径是否位于Android/data之下——是则走uriCacheFileManager,否则走pathCacheFileManager

也就是说:老系统/普通目录 → Path;新系统/沙盒目录 → Uri,用户全程无感知。

Uri 模式的两大成本,作者如何补上

① 授权成本:SAF 必须用户点头

Uri 模式依赖 SAF(Storage Access Framework),权限要通过系统目录选择器授予。UriTool.grantedUriPermission() 会先检查目标 Uri 是否已有持久化授权,没有就弹出"授权提示"对话框,引导用户跳转系统授权页。该检查在 MainActivity 的onStart中每次拉起,防止授权失效后静默失败。

② 速度成本:DocumentFile 遍历非常慢

SAF 的listFiles()每调用一次都要走 ContentProvider,大缓存目录下会卡得明显。作者的三板斧:

  1. 进度反馈:遍历过程中弹窗实时显示3 / 128这类进度(UriCacheFileManager.kt);
  2. MMKV 结果缓存:getCacheMsgByMMKV() 把"章节 → 四件套 Uri + 标题/BV号"的解析结果按 Uri 为 key 存入 MMKV(分钟级过期),二次进入几乎零开销;
  3. 流式转存:合并前由 documentFile2File() 通过openInputStream(uri)把 content Uri 流式写成本地临时 File(1KB 缓冲、每 40KB 回调一次进度),交给 FFmpeg 合并,MergeProgressDialog.java 再按useUri标记选择对应的展平逻辑。

顺带一提,合并完成后的 mp4 分享也踩过权限坑:安卓 7.0+ 直接Uri.fromFile()会抛FileUriExposedException,所以 FileTool.shareFile() 在 SDK ≥ 24 时统一改走 FileProvider,配合 AndroidManifest.xml 中声明的com.molihua.hlbmerge.fileprovider

总结:安卓11+存储访问的3条经验

  • 别假设 File API 万能:安卓 11+ 下其他应用的Android/data沙盒目录,直连读取基本无望,SAF/Uri 才是正路;
  • 路径特征 + 系统版本做自动分流:像本项目一样按underAndroidDataUseUri()动态选路,比让用户手选更省心;
  • 用缓存和流式转存对冲 SAF 的性能税:MMKV 记结果、弹窗给进度、合并前流式落盘,慢也能用。

对于想合并 B 站缓存视频的普通用户,这套双模式设计意味着:无论你的手机是安卓 5 还是安卓 13,只要按弹窗提示完成一次授权,缓存视频就能正常列出并合并导出。

【免费下载链接】BilibiliCacheVideoMerge🔥🔥Android上将bilibili缓存视频合并导出为mp4,支持安卓5.0 ~ 13,视频挂载弹幕播放(Android consolidates and exports the bilibilibili cache video to mp4, supports Android 5.0~13, and plays the video on the screen)项目地址: https://gitcode.com/gh_mirrors/bi/BilibiliCacheVideoMerge

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询