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

Android 13中DownloadManager返回的Uri无法使用问题求助

Android 13 中 DownloadManager 公共目录 Uri 无法启动 ACTION_VIEW 的解决办法

问题描述

我尝试将文件下载到外部公共目录,但downloadManager.getUriForDownloadedFile(requestId)返回的Uri无法使用,无法通过它启动ACTION_VIEW Intent,该流程在Android 10中可正常运行。怀疑与Android 13中缺失更新的权限有关,但Logcat无错误日志。目前可通过setDestinationInExternalFilesDir将文件存储到应用私有目录,再用ContentResolver复制到外部媒体存储实现需求,但代码量较大且繁琐。希望找到使用DownloadManager的setDestinationInExternalPublicDir方式下的解决办法。

现有代码

下载请求创建与入队代码:

val request = DownloadManager
        .Request(Uri.parse(downloadUrl))
        .setDestinationInExternalPublicDir(
            Environment.DIRECTORY_MOVIES,
            "${UUID.randomUUID()}.mp4"
        )
        .setMimeType("video/mp4")
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        .setTitle("Saving...")
        .setRequiresCharging(false)
        .setAllowedOverMetered(true)
        .setAllowedOverRoaming(true)

requestId = downloadManager.enqueue(request)

下载完成监听代码:

private val downloadBroadCastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val requestId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) ?: -1

        val query = DownloadManager.Query()
        query.setFilterById(requestId)

        val cursor = downloadManager.query(query)

        if (cursor.moveToFirst()) {
            val columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)

            when (cursor.getInt(columnIndex)) {
                DownloadManager.STATUS_SUCCESSFUL -> {
                    LOG.info("onReceive: Video download completed!")

                    val uri = downloadManager.getUriForDownloadedFile(requestId)

                    context.startActivity(
                        Intent(Intent.ACTION_VIEW, uri).apply {
                            setDataAndType(uri, "video/mp4")
                        }
                    )
                }
            }
        }   
    }
}

解决办法

1. 适配Android 13媒体权限

Android 13将外部存储权限拆分为媒体类型细分权限,针对视频文件需申请READ_MEDIA_VIDEO权限,在AndroidManifest.xml中添加:

<!-- Android 13+ 视频读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:maxSdkVersion="33" />
<!-- Android 10-12 读取外部存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

同时在运行时动态申请对应权限,危险权限需要用户主动授权。

2. 为Intent添加临时读取权限

DownloadManager返回的Uri默认没有授予其他应用读取权限,需通过Intent.FLAG_GRANT_READ_URI_PERMISSION给视频播放应用临时权限,修改启动Intent的代码:

context?.startActivity(
    Intent(Intent.ACTION_VIEW).apply {
        setDataAndType(uri, "video/mp4")
        // 授予目标应用临时读取权限
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        // Android 12+ 建议添加该FLAG避免上下文启动异常
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    }
)

3. 从Cursor获取路径转换为MediaStore Uri

如果上述方法无效,可从DownloadManager的查询结果中获取文件绝对路径,再通过MediaStore转换为可共享的Content Uri:

when (cursor.getInt(columnIndex)) {
    DownloadManager.STATUS_SUCCESSFUL -> {
        LOG.info("onReceive: Video download completed!")

        // 从Cursor获取文件本地路径
        val pathColumn = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
        val filePath = cursor.getString(pathColumn)
        var fileUri: Uri? = Uri.parse(filePath)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10+ 通过MediaStore获取合法的Content Uri
            val projection = arrayOf(MediaStore.Video.Media._ID)
            val selection = "${MediaStore.Video.Media.DATA} = ?"
            val selectionArgs = arrayOf(filePath.replace("file://", ""))
            
            context?.contentResolver?.query(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                null
            )?.use { videoCursor ->
                if (videoCursor.moveToFirst()) {
                    val id = videoCursor.getLong(videoCursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))
                    fileUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id)
                }
            }
        }

        fileUri?.let { validUri ->
            context?.startActivity(
                Intent(Intent.ACTION_VIEW).apply {
                    setDataAndType(validUri, "video/mp4")
                    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                }
            )
        }
    }
}

4. 验证MimeType与Uri兼容性

确保setMimeType设置的类型与文件实际类型一致,且目标视频应用支持读取content://类型的Uri(Android 10+默认使用该类型Uri)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:15:47