在執行階段要求位置資訊存取權

如果應用程式中的功能需要位置資訊存取權,請等到使用者與該功能互動後再提出權限要求。此工作流程遵循最佳做法,以在情境中要求執行階段權限。如需相關操作說明,請參閱要求應用程式權限指南。

圖 1 說明如何執行此程序的範例。該應用程式包含需要前景位置資訊存取權的「分享位置」功能。但應用程式並不會要求位置存取權,直至使用者選取「分享位置」按鈕。

使用者選取「分享位置」按鈕後,畫面上會顯示系統的位置存取權對話方塊
圖 1 需要前景位置資訊存取權的位置資訊分享功能。如果使用者選取「僅在使用該應用程式時允許」,系統就會啟用這項功能。

使用者只授予大概位置資訊

在 Android 12 (API 級別 31) 以上版本中,即使應用程式要求取得 ACCESS_FINE_LOCATION 執行階段權限,使用者仍可以要求應用程式僅擷取大概位置資訊。

如要處理此潛在的使用者行為,請勿自行要求 ACCESS_FINE_LOCATION 權限。而是在單一執行階段要求中,要求 ACCESS_FINE_LOCATION 權限和 ACCESS_COARSE_LOCATION 權限。如果您嘗試僅要求 ACCESS_FINE_LOCATION,系統會忽略部分 Android 12 版本的要求。如果您的應用程式指定 Android 12 以上版本,系統會在 Logcat 中記錄下列錯誤訊息:

ACCESS_FINE_LOCATION must be requested with ACCESS_COARSE_LOCATION.

如果您的應用程式同時要求 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION,系統權限對話方塊會包含下列選項:

  • 精確:允許應用程式取得精確位置資訊。
  • 概略:允許應用程式只取得大概位置資訊。

圖 3 顯示對話方塊中包含兩個可協助使用者做出選擇的視覺提示選項。使用者決定位置準確度後,只要輕觸三個按鈕中的其中一個,即可選取授予權限的時間長度。

在 Android 12 及以上版本中,無論應用程式的指定 SDK 版本為何,使用者皆可前往系統設定,為任何應用程式設定偏好位置準確度。即使您的應用程式安裝在搭載 Android 11 或以下版本的裝置上也是如此,隨後系統會讓使用者將裝置升級至 Android 12 或以上版本。

對話方塊僅顯示概略位置,且包含上下排列的 3 個按鈕
圖 2. 只有在應用程式要求 ACCESS_COARSE_LOCATION 時,才會顯示系統權限對話方塊。
對話方塊有 2 組上下排的選項
圖 3. 應用程式在單一執行階段要求中同時要求 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
    });
}

要求提升為精確位置

您可以要求使用者將應用程式的存取權從概略位置升級為精確位置。不過,在您要求使用者將應用程式的存取權升級為精確位置前,請考慮您的應用程式用途是否確實需要該等級的精確度。如果您的應用程式需要透過藍牙或 Wi-Fi 與臨近裝置配對,建議使用夥伴模式裝置配對藍牙權限,而非要求 ACCESS_FINE_LOCATION 權限。

如要要求使用者將應用程式位置存取權從概略位置升級至精確位置,請按照下列步驟執行:

  1. 如有需要,請說明應用程式需要該權限的原因
  2. 再次要求 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION 權限。由於使用者已經允許系統向應用程式授予大概位置權限,此時的系統對話方塊有所不同,如圖 4 和圖 5 所示:
對話方塊包含「變更為精確位置」、「僅限這一次」和「拒絕」選項。
圖 4. 使用者先前選取的使用者概略使用應用程式時 (在圖 3 所示對話方塊中)。
對話方塊包含「僅允許這一次」和「拒絕」選項。
圖 5. 使用者先前選取的使用者概略僅允許這一次 (在圖 3 所示對話方塊中)。

最初只請求前景位置資訊

即使應用程式中的多項功能需要位置存取權,其中可能只有部分功能需要背景位置資訊存取權。因此,建議應用程式執行位置存取權漸進式要求,先要求取得前景位置資訊存取權,然後取得背景位置資訊存取權。藉由執行漸進式要求,使用者能進一步瞭解需要背景位置資訊存取權的功能,因此握有更多掌控權和資訊透明度。

圖 6 顯示了專為處理漸進式要求設計的應用程式範例。「顯示目前位置」和「建議臨近位置」功能都需要前景位置資訊存取權。不過,只有「建議附近位置」功能需要背景位置資訊存取權。

啟用前景位置資訊存取權的按鈕與啟用背景位置資訊存取權的按鈕相距半個螢幕
圖 6. 兩項功能均須位置存取權,但只有「建議鄰近功能」功能需要背景位置資訊存取權。

執行漸進式要求的程序如下:

  1. 首先,應用程式應引導使用者存取需要前景位置資訊存取權的功能,如圖 1 中的「分享位置資訊」功能,或圖 2 中的「顯示目前位置資訊」功能。

    在應用程式擁有前景位置資訊存取權之前,建議您禁止使用者存取需要背景位置資訊存取權的功能。

  2. 若使用者日後探索需要背景位置資訊存取權的功能,您可以要求背景位置資訊存取權。

其他資源

如要進一步瞭解 Android 中的位置存取權,請參閱下列資料:

程式碼研究室

影片

範例