借助 Android 10,用户可以更好地控制应用对设备位置信息的访问权限。
当在 Android 10 上运行的应用请求位置信息访问权限时,用户有 3 种选择:
- 始终允许(在前台和后台运行时)
- 仅在使用时允许(仅限在前台运行时)
- 拒绝
在此 Codelab 中,您将学习如何在 Android 10 中快速支持位置信息功能。在此 Codelab 结束时,您应该会得到一个支持“仅限在前台运行时获取位置信息”和“在前台和后台运行时获取位置信息”模式的应用(遵循关于“仅在使用时允许”和“始终允许”位置信息访问权限的最佳做法)。
如果您在学习此 Codelab 时遇到任何问题(代码错误、语法错误、内容含义不清等),请通过 Codelab 左下角的报告错误链接报告相应问题。
前提条件
熟悉 Android 开发,并对位置信息服务有一定了解。
学习内容
- 关于 Android 10 中的位置信息功能的最佳做法
- 如何处理仅限在前台运行时获取位置信息的权限
- 如何处理在前台和后台运行时获取位置信息的权限
您将构建的内容
您将通过以下方式修改一款现有的位置信息应用,使其支持 Android 10:
- 添加对仅限在前台运行时获取位置信息(或“仅在使用时允许”访问权限)的支持。
- 添加对在前台和后台运行时获取位置信息(或“始终允许”访问权限)的支持。
所需条件
- Android Studio 3.5 或更高版本,用于运行代码
- 搭载 Android 10 开发者预览版的设备/模拟器
该应用的界面非常简单:
- 支持仅前台位置信息更新(通过点按第一个按钮)。
- 支持前台和后台位置信息更新(通过点按第二个按钮)。
- 信息会显示在按钮下方。
概念和设置
概念
此 Codelab 重点介绍 Android 10 的不同之处,以及如何提供相关支持。我们已为您完成了大部分与位置信息跟踪相关的代码,因为这些代码没有发生太大变化。
在完成此 Codelab 之前,您应该对 Android 中的位置信息有基本的了解。不过,查看一下相关基础知识也没有什么坏处。
基础知识
通过 Android,您的应用可以通过 android.location
软件包中的类访问设备支持的位置信息服务。位置框架的核心组件是 LocationManager
系统服务,它提供了用于确定底层设备位置和方位的 API(如果适用)。
若要从 NETWORK_PROVIDER
或 GPS_PROVIDER
,
接收位置信息更新,您必须通过在 Android 清单文件中分别声明的 ACCESS_COARSE_LOCATION
或 ACCESS_FINE_LOCATION
权限来请求用户权限。如果没有这些权限,应用将在请求位置信息更新时在运行时失败。
Android 10 强化了这一点,为此,它允许用户选择您何时可以访问这些信息(“仅在使用时允许”、“始终允许”、“禁止”)。
为了让用户更好地控制应用对位置信息的访问权限,Android 10 引入了新的位置信息权限 ACCESS_BACKGROUND_LOCATION
.
与现有的 ACCESS_FINE_LOCATION
和 ACCESS_COARSE_LOCATION
权限不同,新权限仅会影响应用在后台运行时对位置信息的访问权。除非应用的某个 activity 可见或应用正在运行前台服务,否则应用将被视为在后台运行。
如果您的应用具有前台服务(以及通知),则无需请求后台位置信息访问权限,但您仍需要声明该前台服务的前台服务类型为 "location"
。
现在,您已经基本了解我们要完成的任务了,接下来,我们开始处理代码!
克隆入门级项目代码库
为帮助您尽快入门,我们准备了一个入门级项目,您可以在此项目的基础上进行构建。如果您已安装 git,只需运行以下命令即可。(您可以在终端/命令行中输入 git --version
进行检查,验证其是否正确执行。)
git clone https://github.com/googlecodelabs/while-in-use-location
如果未安装 git,您可以将项目下载为 ZIP 文件:
导入项目
启动 Android Studio,然后在欢迎屏幕中选择 Open an existing Android Studio project,以打开项目目录。
项目加载完成后,您可能还会看到一条提醒,指出 Git 将不会跟踪所有本地更改。您可以点击右上角的 Ignore 或 X。(您所做的任何更改都不会保存到 Git 代码库中。)
如果您采用的是 Android 视图,那么在项目窗口的左上角应该会看到类似下图所示的内容。(如果您采用的是 Project 视图,那么需要展开项目才能看到这些内容。)
您可以看到两个文件夹图标(base
和 complete
)。它们都称为“模块”。
请注意,首次打开项目时,Android Studio 可能需要数秒时间在后台编译项目。在此期间,您会在 Android Studio 底部的状态栏中看到一个旋转图标:
建议您等待此过程完成后再更改代码。这样,Android Studio 就可以提取所有必要的组件。
此外,如果您看到 Reload for language changes to take effect? 的提示或类似内容,请选择 Yes。
了解入门级项目
现在,您已经完成准备工作,可以开始在 Android 10 中支持位置信息功能了。我们将使用 base
模块,并以此为起点添加对仅限在前台运行时使用(或“仅在使用时允许”)位置信息的支持。换句话说,您将在每个步骤向 base
中添加代码。
complete
模块可用于检查您的工作,或在您遇到问题时提供参考。
关键组件概览:
MainActivity
:用户用于开始/停止跟踪位置信息的界面(仅限在前台运行时以及在前台和后台运行时)。LocationService
:用于跟踪仅限在前台运行时使用的位置信息的服务,如果应用进入后台且已启用位置信息跟踪功能,该服务会将自身晋升为前台服务(通过通知)。Util
:为 Location 类添加扩展函数,并将位置信息保存在 SharedPreferences(简化数据层)中。
模拟器设置
如果您在设置 Android 模拟器时需要帮助,请参阅运行应用一文。
运行入门级项目
现在,我们来运行应用。
- 将您的 Android 设备连接到计算机或启动模拟器。(请注意,这仅适用于搭载 Android 10 及更高版本的设备)。
- 在工具栏中,从下拉选择器中选择
base
配置,然后点击旁边的“运行”(绿色三角形)按钮:
- 您应该会看到以下应用:
- 您可以随意点按位置信息按钮。该应用已正常运行,并可针对前台以及前台和后台位置信息提供位置信息跟踪服务。现在,我们要让该应用能够在 Android 10 上运行。
在本部分中,您将以 Android 10 为目标平台,并添加对仅限在前台运行时跟踪位置信息的功能(或“仅在使用时允许”访问权限)的支持。
仅限在前台运行时是什么意思?这意味着在前台运行时,应用会跟踪位置信息。也就是说,有某个 activity 位于前台,或者该应用的某项服务正在前台运行(通过通知)。
我们的应用已经提供了一项功能,能够使用在前台运行的 activity 或服务来跟踪用户,因此您无需执行大量操作。您的应用可能也是这样的,因为这是频繁获取位置信息更新的最佳做法。
实际上,您只需指明您的前台服务会被用于位置用途即可。
现在,我们来添加相关支持。
以 SDK 29 为目标版本
在 base
模块的 build.gradle
文件中,搜索 TODO: Step 2.1, Target SDK 29。
执行以下更改:
- 将 compileSdkVersion 更改为
29
- 将 buildToolsVersion 更改为
"29.0.2"
(带引号) - 将 targetSdkVersion 更改为
29
现在,您的代码应如下所示:
android {
// TODO: Step 2.1, Target Android 10.
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.android.whileinuselocation"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
...
}
之后,系统会要求您同步项目。点击 Sync Now。
之后,您的应用就支持 Android 10 了。
添加前台服务类型
在 Android 10 中,如果您需要“仅在使用时允许”位置信息权限,则需要添加前台服务的类型。在我们的示例中,系统会利用该类型来跟踪位置信息。
在 base
模块的 AndroidManifest.xml
中,搜索 TODO: 2.2, Add foreground service type,并将以下代码添加到 <service>
元素中:
android:foregroundServiceType="location"
现在,您的代码应如下所示:
<application>
...
<!-- Foreground services in Android 10+ require type. -->
<!-- TODO: 2.2, Add foreground service type. -->
<service
android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
</application>
大功告成!现在,只需遵循 Android 位置信息功能方面的的最佳做法,即可让您的应用支持 Android 10 的“仅在使用时允许”位置信息功能。
运行应用
从 Android Studio 运行应用,并尝试点按仅限在前台运行时位置信息功能按钮。
所有功能都应该像以前一样工作,只不过现在可以在 Android 10 上运行了。如果您之前未接受过关于位置信息的权限请求,您现在应该会看到新的权限屏幕!
在本部分中,您将添加对在 Android 10 中从后台检索位置信息的支持。通常情况下,您应该优先考虑支持仅限在前台运行时获取位置信息,但在一些用例(例如地理围栏)中,您确实需要在后台获取位置信息。
在清单中添加后台权限
Android 10 包含一项关于在后台获取位置信息的新权限,您必须将该权限添加到清单。
在 base
模块的 AndroidManifest
中,搜索 TODO: 3.1, Add background uses-permission to manifest。
在注解下方添加新的 <uses-permission>
元素:
<!-- TODO: 3.1, Add background uses-permission to manifest. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
现在,您即可请求始终获取设备位置信息(包括在后台运行时)。现在,我们来添加关于该权限的检查和请求。
针对 Android 10 添加代码检查
在 base
模块的 MainActivity.kt
中,搜索 Step 3.2, add check for devices with Android 10,并将当前版本替换为以下代码。
// TODO: Step 3.2, add check for devices with Android 10.
private val runningQOrLater =
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q
我们将添加代码以检查并请求新的后台位置权限。不过,除非您是在 Android 10 或更高版本上运行,否则,您并不需要使用这些代码。
现在,您可以仅在 Android 10 中包含这些新的检查/请求了。首先,我们要添加一些代码。
添加关于后台位置信息访问权限的检查
在 MainActivity.kt
中,搜索 TODO: Step 3.3, Add permission check for background permission,然后将注解正下方的代码行替换为以下代码:
// TODO: Step 3.3, Add check for background permission.
val backgroundPermissionApproved =
if (runningQOrLater) {
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
} else {
true
}
如果您习惯于在 Android 中检查权限,应该会觉得这些代码非常眼熟。唯一的区别在于,您现在要检查的是 ACCESS_BACKGROUND_LOCATION
权限。
我们将使用 Kotlin 魔法命令来实现以下目的:
- 检查我们搭载的系统是不是 Android 10(通过上一步)。
- 如果是,则照常检查该权限。
- 如果不是,我们会返回 true,因为 Android 10 之前的版本中不存在后台位置信息权限。
在以下代码行(已包含在项目中)中,您可以看到,我们只需针对前台 (ACCESS_FINE_LOCATION
) 和后台 (ACCESS_BACKGROUND_LOCATION
) 位置信息权限使用 && 运算符,即可确定应用是否具备始终获取设备位置信息的权限。
现在,我们知道了用户是否已批准在后台获取位置信息,但我们还需要让用户能够实际批准访问权限。
添加关于后台位置信息访问权限的请求
在 MainActivity.kt
中,搜索 TODO: Step 3.4, Add another entry to permission request array,并添加以下代码:
// TODO: Step 3.4, Add another entry to permission request array.
if (runningQOrLater) {
permissionRequests.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
现在,如果您查看完整的代码库,它应该如下所示:
val permissionRequests = arrayListOf(Manifest.permission.ACCESS_FINE_LOCATION)
// TODO: Step 3.4, Add another entry to permission request array.
if (runningQOrLater) {
permissionRequests.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
从第一行可以看到,我们将初始化一个 ArrayList
以传递给 ActivityCompat.requestPermissions()
。
在新代码中,我们会检查我们是不是在 Android 10 上运行;如果是,只需向 ArrayList
再添加一个条目,以便在后台获取位置信息即可。
这样就会触发用户选择他们所需的访问权限级别:仅在使用时访问(仅限在前台运行时获取位置信息的权限)或始终访问(在前台和后台运行时获取位置信息的权限)。
还要注意,ActivityCompat.requestPermissions()
实际上并不会获取 ArrayList
,因此我们需要使用 toTypedArray()
将其转换为实际的 Array
。
在 onRequestPermissionsResult() 中检查后台位置信息是否已获批准
即将大功告成!
在 MainActivity.kt
中,再次搜索 TODO: Step 3.5, For Android 10, check if background permissions approved in request code,然后添加以下代码块:
// TODO: Step 3.5, For Android 10, check if background permissions approved in request code.
if(runningQOrLater) {
foregroundAndBackgroundLocationApproved =
foregroundAndBackgroundLocationApproved &&
(grantResults[1] == PackageManager.PERMISSION_GRANTED)
}
此时,我们要检查我们是不是在 Android 10 上运行。如果是,我们将检查第二项权限是否已获批准。您应该还记得,在上一步中,我们的数组中的第二项是 ACCESS_BACKGROUND_LOCATION
。
由于我们已针对 Android 9 或更低版本在代码中检查第一个结果,因此,我们可以利用这一点,只需重复使用同一变量 foregroundAndBackgroundLocationApproved
并借助新权限对其使用 && 即可。毕竟,除非获得这两种权限,否则我们无法确定其已获批准。
完整代码应如下所示:
var foregroundAndBackgroundLocationApproved =
grantResults[0] == PackageManager.PERMISSION_GRANTED
// TODO: Step 3.5, For Android 10, check if background permissions approved in request code.
if(runningQOrLater) {
foregroundAndBackgroundLocationApproved =
foregroundAndBackgroundLocationApproved &&
(grantResults[1] == PackageManager.PERMISSION_GRANTED)
}
查看前台和后台对位置信息的调用
该步骤中无需添加任何代码。我们只需再查看一行代码即可。
搜索 TODO: Step 3.6, review method call for foreground and background location(就在您刚刚添加的代码下方,间隔几行代码)。
这就是在确认已获得所有权限后,用于开始跟踪位置信息的调用。
// TODO: Step 3.6, review method call for foreground and background location.
foregroundAndBackgroundLocationApproved ->
startForegroundAndBackgroundLocation()
好了,大功告成!
现在,我们来试用一下我们的应用。请注意,如果您在操作过程中遇到问题,请查看 complete
模块。其中包含所有已完成/可运行的代码。
运行应用
现在,您的应用已完全支持 Android 10 位置信息功能。我们来试用一下吧!
从 Android Studio 运行您的应用。现在,如果您点击在前台和后台使用位置信息,您应该会看到以下屏幕之一(具体取决于已获批准的权限):
您会看到,在获得权限后,您的应用会开始显示相关更新。
通过按照本指南中显示的方法来检查和请求位置权限,您的应用可以成功跟踪其有关设备位置信息的访问权限等级。
若要详细了解如何确保用户数据安全,请参阅权限最佳做法指南。
仅请求您所需的权限
仅在需要时请求权限。例如:
- 请勿在应用启动时请求位置权限(除非绝对有必要)。
- 如果您的应用以 Android 10 为目标平台,并且仅在前台运行时需要访问位置信息,请不要请求
ACCESS_BACKGROUND_LOCATION
。
在未授予权限时支持优雅降级
为了保持良好的用户体验,请在设计应用时确保它可以优雅地处理以下情况:
- 应用无法访问位置信息。
- 应用在后台运行时无法访问位置信息。
总结
在这一步骤中,您学习到了以下内容:
- 关于 Android 中的位置信息功能的最佳做法
您已经学习了如何针对 Android 10 接收位置信息更新,并牢记平台的相关最佳做法!
如需了解详情,请访问以下资源: