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

Android 12/13无法选择传递图库视频URI及显示问题求助

问题:Android 12/13从图库选视频后黑屏,触发SecurityException

开发Android应用时,点击图库图标用ACTION_PICK选择视频并传递到编辑界面,在Android 12和13中视频显示黑屏,LogCat抛出如下权限异常:

java.lang.SecurityException: 
UID 10170 does not have permission to content:

//com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F25/ORIGINAL/NONE/video%2Fmp4/1574077794 

[user 0] at android.os.Parcel.createExceptionOrNull 

相关代码

VideoFragment.kt

R.id.imageViewVideo -> {
    if (android.os.Build.VERSION.SDK_INT >= 32) {
        val intent = Intent(Intent.ACTION_PICK, null) // 仅显示视频
        intent.type = "video/*"
        resultLauncher.launch(intent)
    } else {
        requestPermission()
    }
}

private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        val data: Intent? = result.data
        val videoUri: Uri = data?.data!!
        Log.i("videoUri", videoUri.toString()) // content://com.google.android.apps.photos.contentprovider/...
        val videoPath = parsePath(videoUri)
        if (videoPath != null) {
            Log.i("videoPath", videoPath) // /storage/emulated/0/Movies/VID_20231115_163657.mp4
            Log.i("videoPath22", videoUri.path.toString()) // /-1/2/content://media/external/video/media/...
        }

        val intent = Intent(this@Camera2VideoFragment.requireContext(), SelectVideoActivity::class.java)
        intent.putExtra("path", videoPath)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(intent)
    }
}

SelectVideoActivity.kt

// onCreate方法中
val source = File(intent!!.getStringExtra("path")!!)
copyFile(source, inputFile)
Log.i("input file ===", inputFile.toString())

@Throws(IOException::class)
fun copyFile(sourceFile: File?, destFile: File) {
    try {
        if (!destFile.parentFile!!.exists()) destFile.parentFile!!.mkdirs()
        if (!destFile.exists()) {
            destFile.createNewFile()
        }
        var source: FileChannel? = null
        var destination: FileChannel? = null

        source = FileInputStream(sourceFile).channel
        destination = FileOutputStream(destFile).channel
        destination.transferFrom(source, 0, source.size())
    } catch (e:Exception){
        Log.i("error","selectvideo ${e.message}")
    } finally {
        initViews()
        setListener()
    }
}

低版本Android正常运行情况

文件路径示例:

/storage/emulated/0/DCIM/Camera/VID_20231121_193407.mp4 
/storage/emulated/0/Android/data/com.app.packagename/files/app-name/1700718359149.mp4

相关代码:

private lateinit var inputFile: File

// onCreate方法中
inputFile = File(getExternalFilesDir("slow-mow")!!.absolutePath.plus("/" + "${System.currentTimeMillis()}.mp4"))

问题原因&修复方案

核心问题

Android 12+(API32+)的分区存储机制限制了直接通过文件路径访问媒体文件的权限,你通过parsePath解析Content URI得到的路径,在高版本系统中并没有有效的访问权限,触发了SecurityException;同时你给启动Intent加了FLAG_GRANT_READ_URI_PERMISSION,但传递的是文件路径而非原始Content URI,导致权限并未生效。

修复步骤

  1. 直接传递Content URI而非文件路径,确保权限能正确授予给目标Activity
  2. 使用ContentResolver读取URI内容,替代直接操作FileInputStream的方式,适配高版本权限机制

修改后的代码

VideoFragment.kt的resultLauncher部分
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        val data: Intent? = result.data
        val videoUri: Uri = data?.data ?: return@registerForActivityResult
        
        val intent = Intent(requireContext(), SelectVideoActivity::class.java)
        intent.data = videoUri // 传递原始Content URI
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予读取权限
        startActivity(intent)
    }
}
SelectVideoActivity.kt部分
private lateinit var inputFile: File

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...其他初始化代码
    val videoUri: Uri? = intent.data
    videoUri?.let {
        inputFile = File(getExternalFilesDir("slow-mow")!!.absolutePath + "/" + "${System.currentTimeMillis()}.mp4")
        copyFile(it, inputFile)
        Log.i("input file ===", inputFile.toString())
    }
}

@Throws(IOException::class)
fun copyFile(sourceUri: Uri, destFile: File) {
    try {
        if (!destFile.parentFile!!.exists()) destFile.parentFile!!.mkdirs()
        if (!destFile.exists()) {
            destFile.createNewFile()
        }
        // 使用ContentResolver读取URI内容
        contentResolver.openInputStream(sourceUri)?.use { inputStream ->
            FileOutputStream(destFile).use { outputStream ->
                inputStream.copyTo(outputStream)
            }
        }
    } catch (e: Exception) {
        Log.i("error", "selectvideo ${e.message}")
    } finally {
        initViews()
        setListener()
    }
}

额外说明

  • 不需要再调用parsePath解析路径,Android 10+推荐始终通过Content URI访问媒体文件
  • FLAG_GRANT_READ_URI_PERMISSION会让目标Activity获得临时读取该URI的权限,足够完成文件复制操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 15:04:58