我通过以下方式在屏幕"a"中接收uri:

val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
        if(activityResult.resultCode == Activity.RESULT_OK) {
            val uri = activityResult.data?.data!!
            context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
            viewModel.onUriReceived(uri)
        }
    }
LaunchedEffect(launcher) {
     val intent = Intent(Intent.ACTION_OPEN_DOCUMENT, MediaStore.Video.Media.EXTERNAL_CONTENT_URI).apply {
         addCategory(Intent.CATEGORY_OPENABLE)
         type = "video/*"
         addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
         addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
     }
     coroutineScope.launch {
         launcher.launch(intent)
     }
  }

我可以在屏幕"A"中打开uri,但如果我将uri传递到屏幕"B",我会收到:

 java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/video:38 from pid=6074, uid=10146 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
        at android.os.Parcel.createExceptionOrNull(Parcel.java:2425)
        at android.os.Parcel.createException(Parcel.java:2409)
        at android.os.Parcel.readException(Parcel.java:2392)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
        at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:153)
        at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:780)
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2027)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1842)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:1518)

Github repo复制错误:https://github.com/geckogecko/PermissionDenialPlayground

推荐答案

如果判断it.getString("imageUri")的内容,您将看到以下内容:

content://com.android.providers.media.documents/document/image:20

如果您try 使用以下代码对媒体uri进行编码/解码:

val encoded = Uri.encode(uri.toString())
val decoded = Uri.decode(encoded)
val decoded2 = Uri.decode(decoded)
println("original: $uri")
println(" encoded: $encoded")
println(" decoded: $decoded")
println("decodedSecond: $decodedSecond")

你应该看到:

original: content://com.android.providers.media.documents/document/image%3A20
 encoded: content%3A%2F%2Fcom.android.providers.media.documents%2Fdocument%2Fimage%253A20
 decoded: content://com.android.providers.media.documents/document/image%3A20
decoded2: content://com.android.providers.media.documents/document/image:20

Android navigation会自行解码参数,看起来它会多次解码,因为你会看到navigation参数等于结果decoded2而不是decoded.

原因是原始uri包含编码符号'%',因此在第二解码阶段之后'%3A'变为':'.

一个可能的解决方案是为这个符号添加你自己的额外"编码".我 Select '|'是因为我希望它不会出现在android系统提供给你的任何uri中,但如果你有问题,你可以想出另一个字符.

val encoded = Uri.encode(uri.toString().replace('%','|'))
navController.navigate("screenB?imageUri=$encoded")

解码:

val uri = it.arguments?.let {
    it.getString("imageUri")
        ?.replace('|','%')
        ?.let(Uri::parse)
}

另一种 Select 是不编码它,而是将它存储在存储库中的某个容器中,以某种方式在路由之间共享——它可能是singleton或Hilt DI等.因此,您可以将容器id作为参数传递,通常这是 compose 导航的推荐方法.

Android相关问答推荐

无法理解Kotlin Coroutines and Flows中的J.C.编程行为

长流与长流的比较<>

Yarn 机器人导致活动未找到,但Gradlew Run工作正常

OverridePendingTransition已弃用,我该怎么办?

Jetpack Compose中尺寸不断增加的动画

不能有意地从一个活动的可组合功能转移到另一个活动

如何在Jetpack Compose中向SearchBar添加边框

需要 java 17 而不是 java 11:Android CI-CD GitHub Actions

了解 CoroutineScope(Job() + Dispatchers.Main) 语法

如何在 Android Studio 中为带有 Room 的 SQLite 编写需要参数的查询?

具有管理员权限的 Kotlin 中的多用户系统

Kotlin File(path).walkTopDown() 找不到文件

Koin Android-KMM:我有嵌套范围但注入不起作用

在 Jetpack Compose 中自动滚动后面的项目

如何在 Jetpack Compose 中禁用 TabRow 或 Tab 中的涟漪效应?

在 Compose 中使用 DeepLink 会导致无法向后导航

Android全屏AlertDialog

如何满足设备内框架的无效 Wear OS 屏幕截图Wear OS 表盘策略违规?

如何优化 JetPack Compose 列表性能?

dagger2 抛出错误:如果没有 @Provides-annotated 方法就无法提供.在我的 android 项目的构建中