Android:从一项活动到另一项活动的 Uri 访问生命周期

发布于 2025-01-17 09:36:40 字数 2158 浏览 1 评论 0原文

我有一个用于选择图像和视频的辅助方法,我们将其称为Activity B

所以,这就是它的工作原理:

// were in Activity A
// user wants to choose a video
startActivityB(callbacks);

------------------------------------

// were in Activity B now

Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
chooseVideoLauncher.launch(intent);

------------------------------------

// were in chooseVideoLauncher now
Uri videoURI = ...;
callbacks.passVideoURI(videoURI); // this way, Activity A gets the videoURI

// do some more things...

finish(); // so the helper activity B is finished now, as the video is chosen already


------------------------------------

// were in activity A again, but now we have the videoURI
// user clicked a button: "Upload video"
uploadVideo(videoURI);

这是我得到的错误: java.lang.SecurityException:权限拒绝:从 ProcessRecord{f5899ab 29899:com.xxx} (pid=xxx, uid=xxx) 打开提供程序 com.miui.gallery.provider.GalleryOpenProvider,该提供程序未从 UID xxx 导出< /code>

我用谷歌搜索了错误并发现了这个SO线程:此处

@CommonsWare 在评论中解释了错误并链接了他的博客文章:Uri 访问生命周期:短于您可能会认为


所以发生错误是因为帮助程序Activity B 选择了该文件,因此访问权限与Activity B 相关联。没有其他活动可以访问,一旦 Activity B 被销毁(我的代码中发生的情况),对 videoURI 的访问就完全消失了。因此,当我稍后尝试上传视频时,它会抛出此错误。

我尝试了以下解决方案:

  1. 创建视频的本地副本并将该副本传递给 Activity A。这可行,但却是一个糟糕的解决方案。对于较长的视频,应用程序会因内存溢出而崩溃。所以这不是一个选择。
  2. 设置@CommonsWare 提到的标志。所以代码看起来像这样:
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
intent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
chooseVideoLauncher.launch(intent);

但这似乎没有改变任何东西。错误消息仍然完全相同。我是否设置错误?


@CommonsWare 说使用服务也是一种解决方案。我不想纯粹为了修复此权限错误而创建服务。如果没有其他解决办法,我当然会。

但是没有办法也向该 Uri 授予 Activity A 权限吗?

I have a helper method for choosing images and videos, let's call it Activity B.

So, this is how it works:

// were in Activity A
// user wants to choose a video
startActivityB(callbacks);

------------------------------------

// were in Activity B now

Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
chooseVideoLauncher.launch(intent);

------------------------------------

// were in chooseVideoLauncher now
Uri videoURI = ...;
callbacks.passVideoURI(videoURI); // this way, Activity A gets the videoURI

// do some more things...

finish(); // so the helper activity B is finished now, as the video is chosen already


------------------------------------

// were in activity A again, but now we have the videoURI
// user clicked a button: "Upload video"
uploadVideo(videoURI);

This is the error I get:
java.lang.SecurityException: Permission Denial: opening provider com.miui.gallery.provider.GalleryOpenProvider from ProcessRecord{f5899ab 29899:com.xxx} (pid=xxx, uid=xxx) that is not exported from UID xxx

I have googled the error and found this SO thread: here

@CommonsWare explains the error in a comment and links his blog post: Uri Access Lifetime: Shorter Than You Might Think


So the error happens because the helper Activity B chose the file, so the access is tied to Activity B. No other activity has access, and as soon as Activity B is destroyed (what happens in my code), the access to videoURI is completely gone. So when I later try to upload the video, it throws this error.

I tried these solutions:

  1. Create a local copy of the video and pass that copy to Activity A. This works, but is a bad solution. For longer videos the app crashes with a memory overflow. So it's not an option.
  2. Setting the flags @CommonsWare mentioned. So the code looks like this:
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
intent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
chooseVideoLauncher.launch(intent);

But this doesn't seem to change anything. The error message remains exactly the same. Am I setting them wrong?


@CommonsWare says using a service would also be a solution. I would prefer not to create a service purely for fixing this permission error. If there's no other solution, I will of course.

But is there no way to grant Activity A permission to that Uri as well?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鼻尖触碰 2025-01-24 09:36:40

到目前为止,最好的解决方案是将活动 A 和活动 B 合并为一个活动。对单独的屏幕使用片段或可组合项。

设置@CommonsWare提到的标志。所以代码如下所示:

您不会在 ACTION_PICK Intent 上设置标志。相反,活动 B 需要启动活动 A(除了 finish() 之外)。您可以将 Uri 放入 Intent 的“数据”方面(例如,通过 setData()),然后将标志放入关于那个意图。您还需要诸如 FLAG_ACTIVITY_REORDER_TO_FRONT 之类的东西,或者避免在返回堆栈上有 Activity A 的两个副本的东西。

The best solution, by far, is to combine Activity A and Activity B into a single activity. Use fragments or composables for separate screens.

Setting the flags @CommonsWare mentioned. So the code looks like this:

You would not set the flags on the ACTION_PICK Intent. Instead, Activity B needs to start Activity A (in addition to finish()). You would put the Uri into the "data" facet of the Intent (e.g., via setData()), and you would put the flags on that Intent. You would also need something like FLAG_ACTIVITY_REORDER_TO_FRONT or something to avoid having two copies of Activity A on the back stack.

哑剧 2025-01-24 09:36:40

对于第二种方式设置标志以获得长生命周期,您可以尝试添加以下代码,以获得权限,然后启动活动

val contentResolver = applicationContext.contentResolver

val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

For the second way Set the flags to get the long lifetime, you can try to add the following code, to get the permission, then start the activity

val contentResolver = applicationContext.contentResolver

val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文