安卓 - 无法打开内容:file:///storage/emulated/0

19

我有一个带有九宫格图片背景的小部件。

这个图片保存在 /sdcard/mydir/bgs 中。

当我试图使用 setImageViewUri 方法加载图片时,出现以下错误:

Unable to open content: file:///storage/emulated/0/sdcard/mydir/bgs

然后

...

open failed: EACCES (Permission denied)

这仅在主屏幕上出现,并且仅适用于Nexus 10和Nexus 7(使用最新的4.4启动器时,此错误不存在)。 我的应用程序中还有一些RemoteViews,并且所有内容都可以正常工作。

我也已经将READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE添加到清单中。

我该如何解决?

更新:我已检查了setImageViewUri方法,并发现它更改了我的文件路径。

if (value != null) {
            // Resolve any filesystem path before sending remotely
            value = value.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                value.checkFileUriExposed("RemoteViews.setUri()");
            }
        }

这个方法接收了我的值(/sdcard/mydir/bgs),并将它改变为(storage/emulated/0/sdcard/mydir/bgs)。但是,通过adb在系统中找不到该文件。


请分享您的代码,并发布完整的logCat输出。 - Vipul Purohit
4个回答

18

从Lollipop版本开始,Google推出了一种新的方式,可以明确地授予应用程序您希望它们在设备上使用的权限,并禁用您想要拒绝的权限。如果您注意到,在Android监视器中,日志显示

java.io.FileNotFoundException: /storage/emulated/0/advert.mp4: open failed: EACCES (Permission denied)

EACCESS permission denied是导致Java中臭名昭著的FileNotFoundException的原因。

要解决这个问题,只需进入应用程序权限并为您的应用启用存储即可。

输入图像描述


默认情况下,所有权限都被禁用了?!用户每次安装应用程序时都必须启用所有权限吗?我从来没有在我的手机上这样做过(版本6.0.1)... - Matt
请勿在发布的应用程序上执行此操作。您的用户将不会感谢您。相反,您应该在应用程序内部运行时请求权限。https://developer.android.com/training/permissions/requesting.html - Kuffs

13
你需要

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

在你的清单文件中。


1
我已经添加了它。可以是读取和写入权限。 - CeccoCQ

6

似乎使用setImageViewUrifile:// uri不再安全。

为什么?

Jellybean引入了READ_EXTERNAL_STORAGE权限。想要从外部存储读取数据的应用程序必须获得此权限,直到KitKat之前这并不是默认强制执行的。

启动器不持有此权限。实际上,您不能保证任何附加到其中的RemoteView都持有该权限,这使得使用setImageViewUri变得不安全,因为您不知道远程图像视图是否能够读取给定的uri。

现在怎么办?

选项1:使用setImageViewBitmap。

可能由于失败的binder事务而离开了此选项。解决这些问题的诀窍就在于确保您的图像小于1 MB。这并不难。您可以精确计算出您的图像的确切大小。例如,如果您使用ARGB_8888图像,则每个像素需要4个字节。我们可以通过以下方式计算最大大小:

1 MB = 1048576字节= 262144像素= 512 x 512像素

当然,您可以使用RGB_565来挤出更多像素。

还要注意,如果您的小部件很小,则可能不需要太大的图片。您的应用程序小部件可以通过以下方式查询其特定选项:

Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetIds[i]);

int minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);

int maxWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int maxHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);

请注意,返回的值是dip而不是像素,因此您需要将它们转换为适应您的位图。

选项2:使用内容提供程序

如果出于某些原因您仍然不喜欢IPC限制,您可以构建一个自定义内容提供程序。 将该uri传递到setImageViewUri中,然后您就可以开始了。

路径切换怎么办?

路径切换不是真正的问题。 它看起来是个问题,但仿真实际上运行良好。尝试在//storage/emulated/0/sdcard/mydir/bgs内创建文件,该文件将成功创建。

您会注意到,虽然异常是FileNotFoundException,但消息是Permission Denied。


0

我认为你正在手动输入SD卡路径。与其手动输入SD卡路径,你应该像这样获取存储路径:

File extStore = Environment.getExternalStorageDirectory();
String mPath = extStore.getAbsolutePath() + "/mydir/bgs";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接