AndroidManifest.xmlに
WRITE_EXTERNAL_STORAGE,
MOUNT_UNMOUNT_FILESYSTEMS,
READ_EXTERNAL_STORAGEを含む
uses-permission`を追加しました。
このアプリケーションをNexus5 (Android 6.0)で実行しようとすると、以下のような例外が発生しました。
java.io.IOException: open failed:EACCES (Permission denied)`.
また、別のAndroid携帯(Android 5.1)でも試してみましたが、すべてOKでした。
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
currentPhotoPath = image.getAbsolutePath();
return image;
}
Android 6.0ではパーミッションの違いはありますか?
Android 6.0 (Marshmallow)**では、新しい許可モデルが追加されました。
[http://www.captechconsulting.com/blogs/runtime-permissions-best-practices-and-how-to-gracefully-handle-permission-removal][1]をご覧ください。
そのため、Runtime Permission
を確認する必要があります。
**ランタイムパーミッションとは?
Android 6.0 Marshmallowで、Googleは新しい許可モデルを導入しました。これにより、ユーザーはアプリケーションが特定の許可を要求する理由をよりよく理解できるようになりました。ユーザーはインストール時にすべての許可を受け入れるのではなく、アプリケーションの使用中に必要になった許可を受け入れるように促されるようになりました。
**新しいモデルを導入するタイミングは?
新モデルでは、アプリケーションでバージョン23をターゲットにすることを選択するまで、完全なサポートは必要ありません。バージョン22以下をターゲットにしている場合、アプリケーションはインストール時に、Marshmallow以下のOSを搭載したデバイスと同様に、すべてのパーミッションを要求します。
この情報はこちらから引用しています。
実装方法はこちらのリンクからご確認ください。
Android 6(Marshmallow)**では、インストール時にユーザーがすべての権限を受け入れたとしても、後になってユーザーがその権限の一部を奪うことができるようになりました。
手っ取り早い解決策ですが、お勧めしません: グラドルのtargetSdkVersion
を22
に変更すれば、問題は解決するでしょう。
どのように実装するか(ベストプラクティス)。
1.まず、ユーザーのデバイスがMarshmallowデバイスであるかどうかを判断します。
private boolean shouldAskPermission(){。
return(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP_MR1);
}
2.shouldAskPermission()`がtrueを返した場合は、必要な許可を求めてください。
String[] perms = {"android.permission.WRITE_EXTERNAL_STORAGE"};
int permsRequestCode = 200;
requestPermissions(perms, permsRequestCode);
メソッドrequestPermissions(String[] permissions, int requestCode);
は、Android Activityクラスの内部にあるパブリックメソッドです。
3.以下のように、onRequestPermissionResultメソッドでリクエストの結果を受け取ることができます。
オーバーライド
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){。
switch(permsRequestCode){
case 200:
boolean writeAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
break;
}
}
結果を受け取った後は、適切に処理する必要があります。
推奨されるパーミッションフロー:。
その他の情報:
Marshmallowデバイスのユーザーは、アプリケーションの設定で危険なパーミッションを取り消すことができるようになります。
Androidでは、一部のパーミッションを "危険"、一部のパーミッションを "通常"と定義しています。どちらもアプリケーションのマニフェストに必要ですが、危険なパーミッションのみランタイムリクエストが必要です。
新しいパーミッションモデル(ランタイムリクエスト)を実装しないことを選択した場合、パーミッションの取り消しは、望ましくないユーザー体験や、場合によってはアプリケーションのクラッシュを引き起こす可能性があります。
以下の表は、現在の危険なパーミッションとそのグループの一覧です。
ユーザーがグループ/カテゴリー内の1つの権限を受け入れた場合、そのグループ全体を受け入れることになります。
出典:http://www.captechconsulting.com.
デクスターライブラリを使用する:
Dexter]4を使用することができます。実行時にパーミッションを要求するプロセスを簡略化するAndroidライブラリです。
また、下位互換性のためにActivityCompat.requestPermissionsを使用することもできます。
の例を参照してください。
private static final int REQUEST_CODE = 0x11;
String[] permissions = {"android.permission.WRITE_EXTERNAL_STORAGE"};
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE); // without sdk version check
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// save file
} else {
Toast.makeText(getApplicationContext(), "PERMISSION_DENIED", Toast.LENGTH_SHORT).show();
}
}
}