Jetpack Compose Android中从URI播放视频遇权限拒绝问题求助
解决ExoPlayer播放文件管理器选择视频时的权限拒绝问题
从你贴出的错误日志来看,核心问题是权限未正确获取:通过文件管理器拿到的content://com.android.providers.media.documents/...这类Uri属于系统媒体提供者,直接访问会触发SecurityException,必须通过ACTION_OPEN_DOCUMENT系列API获取持久访问权限;另外你提到的Navigation传递Uri的问题,也需要通过编码处理来规避特殊字符冲突。下面一步步帮你解决:
1. 用正确方式获取带持久权限的视频Uri
不要直接获取文件Uri,改用ACTION_OPEN_DOCUMENT启动文件选择器,同时申请持久访问权限,这样App重启后也能正常访问该文件:
// 在Compose中注册文件选择器回调 val pickVideoLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? -> uri?.let { selectedUri -> // 申请持久读权限,这是关键! requireContext().contentResolver.takePersistableUriPermission( selectedUri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) // 把Uri传递给VideoView(比如通过ViewModel) viewModel.updateSelectedVideoUri(selectedUri) } } // 触发视频选择的按钮逻辑 Button(onClick = { pickVideoLauncher.launch(arrayOf("video/*")) }) { Text("选择视频") }
2. 修复ExoPlayer的初始化与生命周期管理
你的原代码中DisposableEffect的用法有点问题,调整为更规范的Player生命周期管理,同时用MediaItem来构建媒体源(ExoPlayer推荐写法):
@Composable fun VideoView(context: Context, mediaUri: Uri) { val exoPlayer = remember(context, mediaUri) { SimpleExoPlayer.Builder(context).build().apply { val dataSourceFactory = DefaultDataSourceFactory( context, Util.getUserAgent(context, context.packageName) ) val mediaItem = MediaItem.fromUri(mediaUri) val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory) .createMediaSource(mediaItem) setMediaSource(mediaSource) playWhenReady = true prepare() } } // 渲染PlayerView AndroidView( modifier = Modifier.testTag("VideoPlayer"), factory = { PlayerView(context).apply { player = exoPlayer layoutParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) } } ) // 页面销毁时释放Player资源 DisposableEffect(exoPlayer) { onDispose { exoPlayer.release() } } }
3. 解决Navigation间传递Uri的编码问题
直接传递Uri字符串会因为特殊字符(比如:、/)导致解析失败,需要先编码再传递,接收方再解码:
跳转时传递编码后的Uri:
val encodedUri = Uri.encode(mediaUri.toString()) navController.navigate("video_play_screen?videoUri=$encodedUri")
目标页面接收并解析:
val navBackStackEntry = rememberNavController().currentBackStackEntry!! val encodedUri = navBackStackEntry.arguments?.getString("videoUri") val mediaUri = encodedUri?.let { Uri.parse(Uri.decode(it)) }
4. 补充Manifest权限配置
如果你的App需要兼容Android 10(API 29)及以下设备,添加只读外部存储权限(Android 11+无需此权限,ACTION_OPEN_DOCUMENT已足够):
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
按照以上步骤调整后,权限拒绝的问题应该就能解决,同时Uri传递的冲突也会被规避。
内容的提问来源于stack exchange,提问作者Dishant Gandhi
相关产品推荐
相关产品推荐

