且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2021-12-15 22:07:56

看来 setImageViewUri 与 file://uris 一起使用不再安全.

It appears setImageViewUri is no longer safe to use with file:// uris.

为什么?

Jellybean 引入了 READ_EXTERNAL_STORAGE 权限.想要从外部存储读取的应用程序必须持有此权限.在 KitKat 之前,默认情况下不会强制执行此操作.

Jellybean introduced the READ_EXTERNAL_STORAGE permission. Apps that want to read from external storage must hold this permission. This wasn't enforced by default until KitKat.

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

The launcher does not hold this permission. In fact you are not guaranteed that any RemoteView you attach to holds that permission. This makes it unsafe to use setImageViewUri, since you don't know if the remote image view will even be able to read the given uri.

现在怎么办?

选项 1:使用 setImageViewBitmap.

由于活页夹交易失败,您可能已放弃此选项.这些技巧只是确保您的图像小于 1 MB.这并不像听起来那么难.您可以准确计算图像的大小.例如,如果您使用的是 ARGB_8888 图像,这意味着每个像素需要 4 个字节.我们可以通过以下方式计算最大尺寸:

You may have moved away from this option due to failed binder transactions. The trick to those is just making sure your image is under 1 MB. This isn't as hard as it sounds. You can calculate exactly how big your image is going to be. For instance if you are using an ARGB_8888 image that means you'll need 4 bytes per pixel. We can calulate the max size by:

1 Mb = 1048576 字节 = 262144 像素 = 512 x 512 图像

1 Mb = 1048576 bytes = 262144 pixels = 512 x 512 image

当然,您可以使用 RGB_565 从中挤出更多,以获得 2 倍的像素.

Of course you can squeeze more out of it using RGB_565 to get 2x the pixels.

另请注意,如果您的小部件很小,您可能不需要大图像.您的 appwidget 可以通过以下方式询问其具体选项:

Also note that you may not need a huge image if your widget is small. Your appwidget can ask about its specific options by the following:

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);

请注意,返回的值是倾角而不是像素,因此您需要将它们转换为缩放位图.

Just be aware that the returned values are in dip not pixels so you will need to convert them to scale your bitmap.

选项 2:使用内容提供者

如果由于某种原因您仍然不喜欢 IPC 限制,您可以随时构建自定义内容提供程序.将该 uri 传递到 setImageViewUri 中,您应该可以开始了.

If for some reason you still don't like the IPC restriction, you can always build a custom content provider. Pass that uri into the setImageViewUri, and you should be good to go.

路径切换呢?

路径切换不是真正的问题.看起来这是一个问题,但仿真实际上工作正常.尝试在 //storage/emulated/0/sdcard/mydir/bgs 中创建一个文件,该文件会创建得很好.

The path switch is not the real issue. It looks like it is a problem, but the emulation actually works fine. Try creating a file inside //storage/emulated/0/sdcard/mydir/bgs and the file will create just fine.

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

You'll notice that while the exception is a FileNotFoundException the message is Permission Denied.