前置き
2018 年 11 月: 既存のアプリのアップデートで、ターゲット API レベル 26 以降が必須になります。
【引用元】Google Developers Japan: 今後の Google Play でのアプリのセキュリティおよびパフォーマンスの改善について
今後もアップデートしていくに辺り、AndroidアプリのターゲットAPIレベルを26に引き上げました。
しかし、アップデートしてからAndroid Vitalsを見て、対応漏れに気づいたことが1つありました。
それが本記事のFileUriExposedExceptionです。
調べていくと、本来であればAndroid7.0(ターゲットAPIレベル24)で対応すべき内容でした。
Android 7.0 向けのアプリでは、Android フレームワークにより、自身のアプリ外への file:// URI の公開を禁止する StrictMode API ポリシーが適用されます。ファイル URI を含むインテントがアプリからなくなると、FileUriExposedException 例外によりアプリはエラーになります。
アプリ間でファイルを共有するには、content:// URI を送信して、この URI への一時的なアクセス パーミッションを付与する必要があります。このパーミッションを付与する最も簡単な方法は、FileProvider クラスを使用することです。
【引用元】Android 7.0 の動作の変更点 | Android Developers
ということで、FileProviderクラスを使った実装に修正しました。
FileUriExposedException例外対応
共有ディレクトリを定義するXMLの作成
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="album"
path="." />
</paths>
AndroidManifest.xmlにFileProviderクラスを定義する
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
</application>
</manifest>
FileProviderクラスを使った実装に修正する
File file = new File(pathString);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
uri = Uri.fromFile(file);
} else {
uri = FileProvider.getUriForFile(xxxActivity.this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
}
intent.setDataAndType(uri, "image/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_GRANT_READ_URI_PERMISSION
);
startActivity(Intent.createChooser(intent, "アプリケーションを選択"));
参考
読書尚友のAndroid 7.0対応について - hishidaのblog
Android 画像ファイルを扱う際のFileとUriまとめ