実行時に位置情報へのアクセスをリクエストする

アプリ内の機能が位置情報へのアクセスを必要とするときは、ユーザーがその機能を操作してから、権限のリクエストを行います。このワークフローは、状況に応じて実行時の権限をリクエストするおすすめの方法(アプリの権限をリクエストする方法を参照)に従っています。

図 1 に、このプロセスの実行方法の例を示します。このアプリには、フォアグラウンドでの位置情報へのアクセスを必要とする「位置情報を共有」機能が含まれています。ただし、ユーザーが [位置情報を共有] ボタンを選択するまで、アプリは位置情報へのアクセスをリクエストしません。

ユーザーが [位置情報を共有] ボタンを選択すると、位置情報へのアクセスをリクエストするシステム ダイアログが表示されます
図 1. フォアグラウンドでの位置情報へのアクセスを必要とする位置情報共有機能。この機能は、ユーザーが [アプリの使用中のみ許可] を選択した場合に有効になります。

ユーザーはおおよその位置情報の取得のみを許可できる

Android 12(API レベル 31)以降では、ユーザーはアプリがおおよその位置情報のみを取得するようリクエストできます。これは、アプリが実行時の ACCESS_FINE_LOCATION 権限をリクエストした場合も同様です。

このようなユーザーの想定操作を処理するには、ACCESS_FINE_LOCATION 権限を単独でリクエストせず、代わりに ACCESS_FINE_LOCATION 権限と ACCESS_COARSE_LOCATION 権限の両方を 1 つのランタイム リクエストでリクエストしてください。ACCESS_FINE_LOCATION のみをリクエストしようとすると、Android 12 の一部のリリースではそのリクエストが無視されてしまいます。Android 12 以降をターゲットとするアプリの場合は、次のエラー メッセージが Logcat に記録されます。

ACCESS_FINE_LOCATION must be requested with ACCESS_COARSE_LOCATION.

アプリが ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION の両方をリクエストした場合、システム権限ダイアログには次のオプションが表示されます。

  • 正確: アプリに正確な位置情報の取得を許可します。
  • おおよそ: アプリにおおよその位置情報のみの取得を許可します。

図 3 は、ユーザーの選択を容易にするため、両方のオプションに対応するビジュアル キューがダイアログに含まれていることを示しています。ユーザーは、位置情報の精度を決定した後、3 つのボタンのいずれかをタップして、権限を付与する期間を選択します。

Android 12 以降では、アプリのターゲット SDK のバージョンにかかわらず、ユーザーはシステム設定に移動して、アプリごとに適切な位置情報の精度を設定できます。これは、Android 11 以前を搭載しているデバイスにアプリがインストールされ、ユーザーがデバイスを Android 12 以降にアップグレードした場合でも同じです。

このダイアログでは、おおよその位置情報へのアクセス権のみを確認するメッセージが表示され、3 つのボタンが上下に並んでいます
図 2. アプリが ACCESS_COARSE_LOCATION のみをリクエストした場合に表示されるシステム権限ダイアログ。
ダイアログでは 2 つのオプション セットが上下に表示されています
図 3. アプリが 1 つのランタイム リクエストで ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION の両方をリクエストした場合に表示されるシステム権限ダイアログ。

ユーザーの選択は権限の付与に影響

次の表に、ユーザーがランタイム権限ダイアログで選択したオプションに基づいて、システムがアプリに付与する権限を示します。

正確 おおよそ
アプリの使用時のみ ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
ACCESS_COARSE_LOCATION
今回のみ ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
ACCESS_COARSE_LOCATION
許可しない 位置情報の利用を許可しない 位置情報の利用を許可しない

システムがアプリに付与した権限を確認するには、権限リクエストの戻り値を確認します。Jetpack ライブラリを次のようなコードで使用することも、プラットフォーム ライブラリを使用して権限リクエスト コードを自身で管理することもできます。

Kotlin

@RequiresApi(Build.VERSION_CODES.N)
fun requestPermissions() {
    val locationPermissionRequest = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        when {
            permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
                // Precise location access granted.
            }
            permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
                // Only approximate location access granted.
            }
            else -> {
                // No location access granted.
            }
        }
    }

    // Before you perform the actual permission request, check whether your app
    // already has the permissions, and whether your app needs to show a permission
    // rationale dialog. For more details, see Request permissions:
    // https://developer.android.com/training/permissions/requesting#request-permission
    locationPermissionRequest.launch(
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        )
    )
}

Java

private void requestPermissions() {

    ActivityResultLauncher<String[]> locationPermissionRequest =
            registerForActivityResult(new ActivityResultContracts
                            .RequestMultiplePermissions(), result -> {

                Boolean fineLocationGranted = null;
                Boolean coarseLocationGranted = null;

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    fineLocationGranted = result.getOrDefault(
                            Manifest.permission.ACCESS_FINE_LOCATION, false);
                    coarseLocationGranted = result.getOrDefault(
                            Manifest.permission.ACCESS_COARSE_LOCATION,false);
                }

                if (fineLocationGranted != null && fineLocationGranted) {
                    // Precise location access granted.
                } else if (coarseLocationGranted != null && coarseLocationGranted) {
                    // Only approximate location access granted.
                } else {
                    // No location access granted.
                }
            }
        );

    // ...

    // Before you perform the actual permission request, check whether your app
    // already has the permissions, and whether your app needs to show a permission
    // rationale dialog. For more details, see Request permissions.
    locationPermissionRequest.launch(new String[] {
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
    });
}

正確な位置情報へのアップグレードをリクエストする

アプリのアクセス権をおおよその位置情報から正確な位置情報にアップグレードするようユーザーに求めることができます。ただし、アプリのアクセス権を正確な位置情報にアップグレードするようユーザーに依頼する前に、アプリのユースケースでこのレベルの精度が必要不可欠であるかどうかを判断してください。アプリがデバイスを Bluetooth または Wi-Fi 経由で近くのデバイスとペア設定する必要がある場合は、ACCESS_FINE_LOCATION 権限をリクエストする代わりに、コンパニオン デバイスのペア設定または Bluetooth の権限を使用することをおすすめします。

位置情報に対するアプリのアクセス権を「おおよそ」から「正確」にアップグレードするようユーザーにリクエストする方法は次のとおりです。

  1. 必要に応じて、アプリが権限を必要とする理由を説明します。
  2. ACCESS_FINE_LOCATION 権限と ACCESS_COARSE_LOCATION 権限をもう一度リクエストします。ユーザーはすでにアプリにおおよその位置情報へのアクセス権を付与することをシステムに許可しているため、システム ダイアログの内容は前と異なります。図 4 と図 5 をご覧ください。
ダイアログには、[正確な位置情報に切り替える]、[今回のみ]、[許可しない] というオプションがあります。
図 4. ユーザーが以前に [おおよそ] と [アプリの使用時のみ] を選択していた場合(図 3 のダイアログを参照)。
ダイアログには [今回のみ] と [許可しない] のオプションがあります。
図 5. ユーザーが以前に [おおよそ] と [今回のみ] を選択していた場合(図 3 のダイアログを参照)。

最初にフォアグラウンドでの位置情報のみをリクエストする

位置情報へのアクセスが必要な機能がアプリに複数あったとしても、バックグラウンドでの位置情報へのアクセスが必要な機能は多くはありません。このため、アプリで位置情報へのアクセスをリクエストする際には、フォアグラウンドでの位置情報へのアクセスを要求してから、バックグラウンドでの位置情報へのアクセスを要求する「段階的なリクエスト」を実行することをおすすめします。段階的なリクエストを実行すると、アプリのどの機能がバックグラウンドでの位置情報へのアクセスを必要としているかをより正確に把握できるため、ユーザーが細かく管理できるようになり、透明性も増します。

図 6 は、段階的なリクエストを扱うように設計されたアプリの例を示しています。「現在地を表示」と「付近の場所をおすすめ」の両方の機能で、フォアグラウンドでの位置情報へのアクセスが必要です。しかし、バックグラウンドでの位置情報へのアクセスを必要とするのは「付近の場所をおすすめ」機能のみです。

フォアグラウンドでの位置情報へのアクセスを有効にするボタンが、バックグラウンドでの位置情報を有効にするボタンから画面半分の距離にある
図 6. どちらの機能も位置情報へのアクセスを必要としますが、「付近の場所をおすすめ」機能のみがバックグラウンドでの位置情報へのアクセスを必要とします。

段階的なリクエストを実行するプロセスは次のとおりです。

  1. まず、図 1 の「位置情報を共有」機能や図 2 の「現在地を表示」機能など、フォアグラウンドでの位置情報へのアクセスが必要な機能をユーザーに案内します。

    アプリからフォアグラウンドでの位置情報へのアクセスがあるまで、バックグラウンドでの位置情報へのアクセスを必要とする機能へのユーザー アクセスを無効にすることをおすすめします。

  2. 後になって、ユーザーがバックグラウンドでの位置情報へのアクセスを必要とする機能を使用しようとしたときに、バックグラウンドでの位置情報へのアクセスをリクエストできます。

参考情報

Android の位置情報へのアクセスについて詳しくは、以下の資料をご覧ください。

Codelab

動画

サンプル