问题
I've a widget with nine-patch image background. The image was saved in /sdcard/mydir/bgs.
When I try to load a image with setImageViewUri method, I've this error:
Unable to open content: file:///storage/emulated/0/sdcard/mydir/bgs
..
then
...
open failed: EACCES (Permission denied)
This appears only on the the home screen and only with Nexus 10 and Nexus 7 (with latest launcher 4.4 this bug not exist). I've also have some RemoteViews on my application and all works correctly.
I've also added into manifest either READ_EXTERNAL_STORAGE, either WRITE_EXTERNAL_STORAGE.
How can I solve?
UPDATE: I've inspect the method setImageViewUri and I've found that it changes the path of my file.
if (value != null) {
// Resolve any filesystem path before sending remotely
value = value.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
value.checkFileUriExposed("RemoteViews.setUri()");
}
}
This method receive my value (/sdcard/mydir/bgs) and changes it into (storage/emulated/0/sdcard/mydir/bgs). But this file not exists into system via adb.
回答1:
It appears setImageViewUri
is no longer safe to use with file:// uris.
Why?
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.
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.
What now?
Option 1: Use setImageViewBitmap.
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 bytes = 262144 pixels = 512 x 512 image
Of course you can squeeze more out of it using RGB_565 to get 2x the pixels.
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.
Option 2: Use a content provider
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.
What about the path switch?
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.
You'll notice that while the exception is a FileNotFoundException the message is Permission Denied.
回答2:
As from Lollipop, Google introduced a new way of explicitly giving apps the permission you want them to use on your device and disabling the ones you want to deny the app. If you notice, in your android monitor, the log shows
java.io.FileNotFoundException: /storage/emulated/0/advert.mp4: open failed: EACCES (Permission denied)
EACCESS permission denied
is the cause of the java's infamous FileNotFoundException
.
To solve this, just goto your App permission and enable storage for your app
回答3:
You need
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
in your manifest.
回答4:
I think you are entering SD card path manually. Instead of manually entering SD Card path you should get storage path like this :
File extStore = Environment.getExternalStorageDirectory();
String mPath = extStore.getAbsolutePath() + "/mydir/bgs";
来源:https://stackoverflow.com/questions/20631446/android-unable-to-open-content-file-storage-emulated-0