Android アプリにおける実行時権限のスマートな処理手法

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` 文を排除し、保守性と読みやすさを向上させています。

タグ: RxPermissions AndroidRuntimePermissions RxJava2

8月1日 07:35 投稿