请求位置权限

为了保护用户隐私,使用位置服务的应用程序必须请求位置权限。

当您请求位置权限时,请遵循与请求任何其他 运行时权限相同的最佳实践。位置权限的一个重要区别是系统包含多个与位置相关的权限。您请求哪些权限以及如何请求这些权限取决于应用程序用例的位置需求。

本页面介绍了不同类型的定位需求,并提供有关在每种情况下如何请求定位权限的指导。

位置访问类型

每个权限都具有以下特征的组合

前台位置

如果您的应用程序包含仅共享或接收一次位置信息或在限定时间内共享或接收位置信息的特性,则该特性需要前台位置访问权限。以下是一些示例

  • 在导航应用程序中,一个特性允许用户获取逐向导航。
  • 在消息应用程序中,一个特性允许用户与其他用户共享他们的当前位置。

如果您的应用程序的特性在以下情况之一中访问设备的当前位置,则系统将认为您的应用程序正在使用前台位置

  • 属于您的应用程序的活动可见。
  • 您的应用程序正在运行前台服务。当前台服务运行时,系统会通过显示持久通知来提高用户意识。当应用程序被置于后台时,例如当用户按下设备上的主页按钮或关闭设备的显示屏时,您的应用程序将保留访问权限。

    此外,建议您声明 前台服务类型location,如以下代码片段所示。在 Android 10(API 级别 29)及更高版本上,您必须声明此前台服务类型。

    <!-- Recommended for Android 9 (API level 28) and lower. -->
    <!-- Required for Android 10 (API level 29) and higher. -->
    <service
        android:name="MyNavigationService"
        android:foregroundServiceType="location" ... >
        <!-- Any inner elements would go here. -->
    </service>
    

当您的应用程序请求 ACCESS_COARSE_LOCATION 权限或 ACCESS_FINE_LOCATION 权限时,您声明需要前台位置,如以下代码片段所示

<manifest ... >
  <!-- Always include this permission -->
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

  <!-- Include only if your app benefits from precise location access. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

后台位置

如果应用程序中的特性不断与其他用户共享位置或使用 地理围栏 API,则该应用程序需要后台位置访问权限。以下是一些示例

  • 在家庭位置共享应用程序中,一个特性允许用户不断与家人共享位置。
  • 在物联网应用程序中,一个特性允许用户配置家庭设备,以便在用户离开家时关闭,并在用户回家时重新打开。

如果应用程序在 前台位置 部分中描述的任何情况以外的任何情况下访问设备的当前位置,则系统将认为您的应用程序正在使用后台位置。后台位置精度与 前台位置精度 相同,这取决于应用程序声明的位置权限。

在 Android 10(API 级别 29)及更高版本上,您必须在应用程序清单中声明 ACCESS_BACKGROUND_LOCATION 权限才能在运行时 请求后台位置访问权限。在早期版本的 Android 上,当应用程序获得前台位置访问权限时,它会自动获得后台位置访问权限。

<manifest ... >
  <!-- Required only when requesting background location access on
       Android 10 (API level 29) and higher. -->
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

精度

Android 支持以下级别的定位精度

近似
提供设备位置估计值。如果此位置估计值来自 LocationManagerServiceFusedLocationProvider,则此估计值的精度约为 3 平方公里(约 1.2 平方英里)。当您声明 ACCESS_COARSE_LOCATION 权限但未声明 ACCESS_FINE_LOCATION 权限时,您的应用程序可以接收此精度的定位信息。
精确
提供尽可能精确的设备位置估计值。如果位置估计值来自 LocationManagerServiceFusedLocationProvider,则此估计值通常在约 50 米(160 英尺)以内,有时甚至精确到几米(10 英尺)或更精确。当您声明 ACCESS_FINE_LOCATION 权限时,您的应用程序可以接收此精度的定位信息。

如果 用户授予了近似位置权限,则无论应用程序声明了哪些位置权限,您的应用程序都只能访问近似位置。

当用户仅授予近似位置访问权限时,您的应用程序仍然应该可以正常工作。如果应用程序中的特性绝对需要使用 ACCESS_FINE_LOCATION 权限访问精确位置,则可以要求用户 允许应用程序访问精确位置

在运行时请求位置访问权限

当您应用中的某个功能需要访问位置时,请等到用户与该功能交互后再请求权限。此工作流程遵循在上下文中请求运行时权限的最佳实践,如解释如何 请求应用权限 的指南中所述。

图 1 显示了执行此过程的示例。该应用包含一个“共享位置”功能,该功能需要前台位置访问权限。但是,该应用不会请求位置权限,直到用户选择 **共享位置** 按钮。

After the user selects the Share Location button, the
    system's location permission dialog appears
图 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 或更高版本,情况也是如此。

The dialog refers only to approximate location and
         contains 3 buttons, one above the other
图 2. 当您的应用仅请求 ACCESS_COARSE_LOCATION 时出现的系统权限对话框。
The dialog has 2 sets of options, one above the other
图 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

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.
locationPermissionRequest.launch(arrayOf(
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.ACCESS_COARSE_LOCATION))

Java

ActivityResultLauncher<String[]> locationPermissionRequest =
    registerForActivityResult(new ActivityResultContracts
        .RequestMultiplePermissions(), result -> {
            Boolean fineLocationGranted = result.getOrDefault(
                    Manifest.permission.ACCESS_FINE_LOCATION, false);
            Boolean 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 所示
The dialog contains the options 'Change to precise
         location', 'Only this time', and 'Deny'.
图 4. 用户之前选择了 **大致** 和 **在使用应用时**(在图 3 中的对话框中)。
The dialog contains the options 'Only this time' and
         'Deny'.
图 5. 用户之前选择了 **大致** 和 **仅此一次**(在图 3 中的对话框中)。

最初仅请求前台位置

即使您应用中的多个功能需要位置访问权限,也可能只有其中一部分需要后台位置访问权限。因此,建议您的应用对位置权限执行增量请求,先请求前台位置访问权限,然后请求后台位置访问权限。通过执行增量请求,您可以为用户提供更多控制权和透明度,因为他们可以更好地了解您应用中的哪些功能需要后台位置访问权限。

图 6 显示了一个旨在处理增量请求的应用示例。 “显示当前位置”和“推荐附近地点”这两个功能都需要前台位置访问权限。但是,只有“推荐附近地点”功能需要后台位置访问权限。

The button that enables foreground location access is
    positioned half a screen length away from the button that enables background
    location
图 6. 这两个功能都需要位置访问权限,但只有“推荐附近功能”功能需要后台位置访问权限。

执行增量请求的过程如下

  1. 首先,您的应用应引导用户访问需要前台位置访问权限的功能,例如图 1 中的“共享位置”功能或图 2 中的“显示当前位置”功能。

    建议您在应用获得前台位置访问权限之前禁用用户访问需要后台位置访问权限的功能。

  2. 稍后,当用户探索需要后台位置访问权限的功能时,您可以 请求后台位置 访问权限。

如有必要,请求后台位置

图 7. 设置页面包含一个名为 **始终允许** 的选项,该选项授予后台位置访问权限。

权限对话框内容取决于目标 SDK 版本

当您应用中的某个功能在运行 Android 10(API 级别 29)的设备上请求后台位置时,系统权限对话框将包含一个名为 **始终允许** 的选项。如果用户选择此选项,您应用中的功能将获得后台位置访问权限。

但是,在 Android 11(API 级别 30)和更高版本上,系统对话框不包含 **始终允许** 选项。相反,用户必须在设置页面上启用后台位置,如图 7 所示。

您可以通过在请求后台位置权限时遵循最佳实践来帮助用户导航到此设置页面。授予权限的过程取决于您应用的目标 SDK 版本。

应用以 Android 11 或更高版本为目标

如果您的应用尚未获得 ACCESS_BACKGROUND_LOCATION 权限,并且 shouldShowRequestPermissionRationale() 返回 true,请向用户显示一个教育性 UI,其中包含以下内容

  • 清楚地解释为什么您应用的功能需要访问后台位置。
  • 授予后台位置的设置选项的可见用户标签(例如,图 7 中的 **始终允许**)。您可以调用 getBackgroundPermissionOptionLabel() 来获取此标签。此方法的返回值已本地化为用户的设备语言偏好。
  • 用户拒绝权限的选项。如果用户拒绝后台位置访问权限,他们应该能够继续使用您的应用。
Users can tap the system notification to change location
  settings for an app
图 8. 通知提醒用户他们已授予应用后台位置访问权限。

应用以 Android 10 或更低版本为目标

当您应用中的某个功能请求后台位置访问权限时,用户会看到一个系统对话框。此对话框包含一个选项,用于导航到设置页面上的应用位置权限选项。

只要您的应用已经遵循了请求位置权限的最佳实践,您就不需要进行任何更改来支持此行为。

用户可以影响后台位置精度

如果 用户请求大致位置,用户在位置权限对话框中的选择也适用于后台位置。换句话说,如果用户授予您的应用 ACCESS_BACKGROUND_LOCATION 权限,但在前台只授予大致位置访问权限,那么您的应用在后台也只有大致位置访问权限。

后台位置授予的提醒

在 Android 10 及更高版本上,当您应用中的某个功能在用户授予后台位置访问权限后首次从后台访问设备位置时,系统会安排向用户发送通知。此通知会提醒用户,他们已允许您的应用始终访问设备位置。图 8 中显示了一个示例通知。

检查您应用的 SDK 依赖项中的位置要求

检查您的应用是否使用任何依赖于位置权限的 SDK,尤其是 ACCESS_FINE_LOCATION 权限。请参阅这篇关于 了解您的 SDK 依赖项的行为 的 Medium 文章。

其他资源

要详细了解 Android 中的位置权限,请查看以下资料

Codelabs

视频

示例