Android 6.0(APIレベル23)以降、危険度の高い権限(例:電話発信、位置情報取得、ストレージアクセス)はインストール時ではなく、アプリ実行中にユーザーから明示的に許可を得る必要があります。以下は、RxPermissions を活用した簡潔かつ堅牢な動的権限管理実装です。
1. マニフェストへの権限宣言
対象権限を AndroidManifest.xml に追加します(必須の前段階):
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2. 依存ライブラリの導入
Gradle の app/build.gradle に、リアクティブな権限制御を支援するライブラリを追加します:
implementation 'com.github.tbruyelle:rxpermissions:0.12.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
3. 権限管理ユーティリティクラス(再設計版)
以下のクラスは、再帰呼び出しや冗長なロジックを排除し、単一責任原則に基づき再構成されています。複数権限の一括処理と、ユーザー拒否時のナビゲーションを明確に分離しています。
public class RuntimePermissionHandler {
private static final String TAG = "PermHandler";
private final AppCompatActivity hostActivity;
private final RxPermissions rxPermissions;
// 対象権限リスト(必要に応じて拡張可能)
private static final String[] TARGET_PERMISSIONS = {
Manifest.permission.CALL_PHONE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE
};
public RuntimePermissionHandler(AppCompatActivity activity) {
this.hostActivity = activity;
this.rxPermissions = new RxPermissions(activity);
}
/**
* 全権限の一括リクエストと結果ハンドリング
*/
public void requestAllRequired() {
rxPermissions.request(TARGET_PERMISSIONS)
.subscribe(granted -> {
if (granted) {
Log.i(TAG, "全権限が許可されました");
onPermissionsGranted();
} else {
showRationaleDialog();
}
}, throwable -> {
Log.e(TAG, "権限要求中にエラー", throwable);
});
}
/**
* 権限付与後のコールバック(アプリ固有の処理をここに記述)
*/
private void onPermissionsGranted() {
// 例:通話機能を有効化、地図表示開始など
Toast.makeText(hostActivity, "必要な権限が取得されました", Toast.LENGTH_SHORT).show();
}
/**
* 拒否時に表示されるダイアログ(ユーザー誘導用)
*/
private void showRationaleDialog() {
new AlertDialog.Builder(hostActivity)
.setTitle("権限が必要です")
.setMessage("一部機能を利用するには、以下の権限が必要です。設定画面から手動で許可してください。")
.setPositiveButton("設定を開く", (dialog, which) ->
openAppSettings())
.setNegativeButton("後で", null)
.setCancelable(false)
.show();
}
/**
* アプリ設定画面へ遷移
*/
private void openAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", hostActivity.getPackageName(), null);
intent.setData(uri);
hostActivity.startActivity(intent);
}
/**
* 現在の権限状態を確認(非UIスレッドでも安全に使用可能)
*/
public boolean areAllGranted() {
for (String perm : TARGET_PERMISSIONS) {
if (!rxPermissions.isGranted(perm)) {
return false;
}
}
return true;
}
}
4. Activity からの利用方法
権限が必要なタイミング(例:ボタン押下時)で、以下のように呼び出します:
// MainActivity.java 内
findViewById(R.id.btn_make_call).setOnClickListener(v -> {
RuntimePermissionHandler handler = new RuntimePermissionHandler(this);
if (!handler.areAllGranted()) {
handler.requestAllRequired();
} else {
initiatePhoneCall(); // 権限あり → 実行
}
});
この実装は、RxJava の非同期フローを活かしつつ、UI スレッドとの連携を安全に行い、ユーザー体験を損なわないよう配慮されています。また、権限名のハードコードや冗長な `switch` 文を排除し、保守性と読みやすさを向上させています。