You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从SAF文件选择器请求获取文件路径与实例并避免大文件复制卡顿

问题根因

当前的整屏卡顿是因为在主线程执行大文件IO操作,1~2GB的视频全量复制到本地缓存会长时间阻塞主线程,且该复制操作完全没有必要,SAF返回的URI本身就支持直接读取内容,适配Android 4.4到Android 12+全版本无需使用legacy storage相关标志位。

最优实现方案(无需复制文件)
  • 首先要持久化URI权限:你请求时已经添加了FLAG_GRANT_PERSISTABLE_URI_PERMISSION,拿到返回的URI后要主动调用持久化权限方法,即使应用重启也能正常访问该URI,无需用户重复授权。
  • 直接通过URI读取内容上传:不要强行将URI转为本地文件路径,直接通过ContentResolver打开输入流喂给上传服务即可,全程不会产生额外存储占用,也没有大文件复制的耗时开销。
  • 若上传服务仅支持本地文件路径:则必须把复制操作放到后台线程执行(比如协程IO调度器、子线程),复制过程可搭配进度提示,不会阻塞主界面。
代码修改参考

1. URI权限持久化(拿到返回URI后第一时间执行)

uri?.let {
    val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    context.contentResolver.takePersistableUriPermission(it, takeFlags)
}

2. 直接通过URI构造上传请求示例(以OkHttp为例)

不需要生成本地缓存文件,直接读取URI内容上传:

fun buildRequestBodyFromUri(uri: Uri, context: Context): RequestBody {
    val contentResolver = context.contentResolver
    val mimeType = contentResolver.getType(uri) ?: "video/*"
    val fileName = getFileNameFromUriOrNull(uri, contentResolver) ?: "unknown_video"
    return object : RequestBody() {
        override fun contentType() = MediaType.parse(mimeType)
        override fun contentLength() = contentResolver.openFileDescriptor(uri, "r")?.statSize ?: -1
        override fun writeTo(sink: BufferedSink) {
            contentResolver.openInputStream(uri)?.use { input ->
                sink.outputStream().use { output ->
                    input.copyTo(output)
                }
            }
        }
    }
}
注意事项
  • 不要强制把SAF的URI转成本地路径,Android 10+之后第三方应用无权限访问其他应用的私有目录路径,转出来的路径本身无效,强制使用会引发崩溃,通过ContentResolver操作URI是官方唯一推荐的全版本适配方案。
  • 若使用第三方上传SDK,优先查找SDK是否支持传入ContentResolver和URI的接口,目前主流上传SDK都已适配SAF场景。
  • 确实需要本地缓存的场景,必须在后台线程执行文件复制操作,绝对不能在主线程执行IO操作。

内容的提问来源于stack exchange,提问作者ansh sachdeva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 07:54:02