2026-03 技术复盘:Compose 架构下的 URI 权限失效与规避方案

1. 问题现象 (Problem Space)

在 Android 15/16 环境下,从 Gmail 等外部应用通过 Intent 调起单 Activity 架构的 App 时,会出现以下典型错误:

  • 报错信息java.lang.SecurityException: Permission Denial: reading ... requires the provider be exported, or grantUriPermission()
  • 表现:UI 陷入死循环或 NaN 刷屏,ReaderViewModel 异步加载文件时由于权限校验失败导致 openInputStream 抛出异常。

2. 根因分析 (Root Cause Analysis)

  • 授权对象错位:系统将 URI 临时权限授予了 MainActivity 实例。在单 Activity 架构中,URI 常作为 Navigation Argument 传递。当 URI 被 toString() 序列化再重新 Uri.parse() 时,新的 URI 对象丢失了原始 Intent 中的权限令牌 (Permission Token)
  • 异步校验失效ViewModelScope 启动的协程属于异步执行。当底层 ContentProvider 进行权限检查时,如果原始授权的上下文(Context)已进入非活跃状态或令牌未显式传递,异步线程的读取请求会被拦截。

3. 架构级解决方案:预读与沙盒隔离 (The Intake Pattern)

不再依赖 content:// 协议进行跨页面传递,而是在 Activity 层的入口处完成“权限变现”。

A. MainActivity 入口拦截

onCreateonNewIntent 中,利用 Activity 权限尚存的窗口期,立即将流拷贝至 App 私有目录(cacheDir)。

private suspend fun handleExternalPdf(uri: Uri): File? = withContext(Dispatchers.IO) {
    val tempFile = File(cacheDir, "ext_${System.currentTimeMillis()}.pdf")
    try {
        contentResolver.openInputStream(uri)?.use { input ->
            tempFile.outputStream().use { output -> input.copyTo(output) }
        }
        if (tempFile.exists() && tempFile.length() > 0) tempFile else null
    } catch (e: Exception) {
        null
    }
}

B. 导航参数解耦

NavHost 仅传递本地 File.absolutePath。由于本地沙盒文件不涉及 ContentProvider 权限校验,ReaderViewModelPDFView 可以稳定读取。

4. 性能与稳定性优化 (Stability & Performance)

  • NaN 帧率异常规避:在 Compose UI 层,必须对 PDFView 的加载状态进行物理隔离。
    • 逻辑:在 totalPages == 0 时,禁止执行依赖总页数的布局计算(如滚动条 Progress)。
    • 效果:消除 (current / 0) 导致的 NaN 计算,解决 setRequestedFrameRate 刷屏问题。
  • 状态保持 (State Restoration):使用 rememberSaveable 配合自定义 Saver
    • 关键点:保存 lastLoadedFilePath 而非 Uri。在屏幕旋转(Configuration Change)后,对比路径字符串决定是否重新 load(),防止重复拷贝导致的 IO 浪费。

5. 结论 (Conclusion)

单 Activity 架构将权限管理的责任从系统自动分配转嫁给了开发者。通过“Activity 认领 -> 私有化拷贝 -> 路径导航”这一链路,可以彻底解决外部 URI 授权不稳定的问题,并提升 SmartPDF (v0.6) 的加载成功率。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容