9パッチの画像背景を持つウィジェットがあります。画像は/ 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ではこのバグは存在しません)。また、アプリケーションにいくつかのRemoteViewがあり、すべて正常に動作します。
マニフェストに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を介してシステムに存在しません。
setImageViewUri
はfile:// urisで使用するのは安全ではないようです。
なぜ?
JellybeanはREAD_EXTERNAL_STORAGE
パーミッションを導入しました。外部ストレージから読み取りたいアプリは、この許可を保持する必要があります。これは、キットカットまでデフォルトで強制されませんでした。
ランチャーはこの許可を保持していません。実際、接続したRemoteViewがその許可を保持していることは保証されていません。これにより、setImageViewUriを使用しても安全ではなくなります。リモートイメージビューが指定されたuriを読み取ることができるかどうかわからないためです。
今何?
オプション1:setImageViewBitmapを使用します。
バインダートランザクションが失敗したため、このオプションから離れた可能性があります。それらのトリックは、画像が1 MB未満であることを確認することです。これは思ったほど難しくありません。画像の大きさを正確に計算できます。たとえば、ARGB_8888画像を使用している場合、ピクセルごとに4バイトが必要になります。次の方法で最大サイズを計算できます。
1 Mb = 1048576バイト= 262144ピクセル= 512 x 512画像
もちろん、RGB_565を使用して2xのピクセルを取得することにより、さらに絞り出すことができます。
また、ウィジェットが小さい場合、大きな画像は必要ないかもしれないことに注意してください。 appwidgetは、次の方法で特定のオプションについて尋ねることができます。
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);
返される値はピクセルではなくディップであるため、ビットマップを拡大縮小するために変換する必要があることに注意してください。
オプション2:コンテンツプロバイダーを使用する
何らかの理由でIPC制限がまだ気に入らない場合は、いつでもカスタムコンテンツプロバイダーを構築できます。そのURIをsetImageViewUriに渡すと、準備が整います。
パススイッチはどうですか?
パスの切り替えは実際の問題ではありません。問題のように見えますが、実際にはエミュレーションは正常に機能します。 //storage/emulated/0/sdcard/mydir/bgs
内にファイルを作成してみてください。ファイルは問題なく作成されます。
例外はFileNotFoundExceptionですが、メッセージはPermission Deniedです。
Lollipopからのように、Googleは、デバイスで使用する権限をアプリに明示的に付与し、アプリを拒否するアプリを無効にする新しい方法を導入しました。気づいたら、Androidモニターで、ログに
Java.io.FileNotFoundException: /storage/emulated/0/advert.mp4: open failed: EACCES (Permission denied)
EACCESS permission denied
は、Javaの悪名高いFileNotFoundException
の原因です。
これを解決するには、アプリの許可に移動し、アプリのストレージを有効にします
あなたが必要
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
あなたのマニフェストで。
SDカードのパスを手動で入力していると思います。 SDカードのパスを手動で入力する代わりに、次のようなストレージパスを取得する必要があります。
File extStore = Environment.getExternalStorageDirectory();
String mPath = extStore.getAbsolutePath() + "/mydir/bgs";