0Pricing
Android Academy · 课时

正确处理位置权限

了解前台位置和精确位置

正确处理位置权限 是 CoddyKit 上的免费 Android Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

权限不是可选项

位置权限是 Android 最敏感的权限之一。仅在清单中声明还不够——在新版本 Android 上,您必须在运行时请求用户授权,并妥善处理每一种结果。

本课将介绍权限级别、如何在 Compose 中请求权限、如何处理拒绝,以及新的精确与大致位置和后台位置规则。

三种位置权限

Android 将位置权限分为多个级别:

  • ACCESS_COARSE_LOCATION —— 大致位置(约精确到街区)。
  • ACCESS_FINE_LOCATION —— 精确位置(GPS 级别)。
  • ACCESS_BACKGROUND_LOCATION —— 应用不在前台时访问位置。

只请求实际需要的权限。后台位置访问会在 Play 商店触发额外审核。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- only if truly needed: -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

在 Compose 中请求权限

Activity Result API 会为您提供用于显示权限对话框的启动器。在 Compose 中,将 rememberLauncherForActivityResult 与 RequestMultiplePermissions 合约结合使用。

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts

val launcher = rememberLauncherForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { result ->
    val fine = result[Manifest.permission.ACCESS_FINE_LOCATION] == true
    val coarse = result[Manifest.permission.ACCESS_COARSE_LOCATION] == true
    // react to the user's choice
}

发起请求

调用 launch() 并传入权限数组即可触发对话框——通常应在用户点击按钮后触发,而不是在界面加载时自动触发。

Button(onClick = {
    launcher.launch(
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        )
    )
}) {
    Text("Enable location")
}

检查权限是否已授予

如果权限已经授予,请不要再次请求。在发起请求前,使用 ContextCompat.checkSelfPermission 进行检查。

import androidx.core.content.ContextCompat
import android.content.pm.PackageManager

fun hasFineLocation(context: Context): Boolean {
    return ContextCompat.checkSelfPermission(
        context,
        Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED
}

精确位置与大致位置

从 Android 12(API 31)开始,即使您请求的是精确位置,用户也可以授予大致位置权限。因此,您必须同时请求精确和大致位置权限,并接受用户可能将授权降级为大致位置。

请尽可能设计出在大致位置下也能正常工作的功能。

// Request both — the system shows a Precise/Approximate toggle
launcher.launch(
    arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
)
// If only COARSE comes back granted, you got approximate location.

显示权限说明

如果用户拒绝过一次,Android 允许您检查 shouldShowRequestPermissionRationale。当它为 true 时,请在再次请求前,友好地说明您为什么需要位置信息。

import androidx.core.app.ActivityCompat

val shouldExplain = ActivityCompat.shouldShowRequestPermissionRationale(
    activity,
    Manifest.permission.ACCESS_FINE_LOCATION
)
if (shouldExplain) {
    // show a dialog: "We use your location to find nearby stores"
}

处理永久拒绝

如果用户选择不再询问(或拒绝两次),系统将不再显示对话框。您唯一能做的是将用户引导至应用设置,让他们手动启用权限。

import android.content.Intent
import android.provider.Settings
import android.net.Uri

fun openAppSettings(context: Context) {
    val intent = Intent(
        Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
        Uri.fromParts("package", context.packageName, null)
    )
    context.startActivity(intent)
}

使用 Accompanist Permissions

accompanist-permissions 库将上述流程封装成适合 Compose 使用的状态。rememberMultiplePermissionsState 会跟踪权限授予状态,并提供 launchMultiplePermissionRequest() 调用。

import com.google.accompanist.permissions.rememberMultiplePermissionsState

val permissions = rememberMultiplePermissionsState(
    listOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
)

if (permissions.allPermissionsGranted) {
    // show the map / read location
} else {
    Button(onClick = { permissions.launchMultiplePermissionRequest() }) {
        Text("Grant location")
    }
}

后台位置规则

后台位置需要采用两步流程:先授予前台位置权限(精确/大致),然后单独请求 ACCESS_BACKGROUND_LOCATION,该请求会在 Android 11 及更高版本上打开系统设置。Google Play 商店要求您提供明确的使用理由和隐私政策。

只有当您的应用确实需要在关闭状态下获取位置时才请求该权限,例如地理围栏、健身追踪等。

// Step 1: request foreground first and confirm it's granted.
// Step 2: only then request background:
launcher.launch(
    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
)
// On Android 11+ this sends the user to settings to pick "Allow all the time".

受权限控制的地图

将这些部分串联起来:根据权限状态控制地图的显示。只有获得权限后才显示地图,否则显示请求权限的提示。这是标准且尊重用户的实现模式。

@Composable
fun LocationGate(permissions: MultiplePermissionsState) {
    when {
        permissions.allPermissionsGranted -> MapScreen()
        else -> Column {
            Text("We need location to show nearby places.")
            Button(onClick = { permissions.launchMultiplePermissionRequest() }) {
                Text("Allow location")
            }
        }
    }
}

快速检查

从 Android 12 开始,用户可以选择大致位置,而不是精确位置。这对您的权限请求意味着什么?

回顾

现在您已经了解如何负责任地请求位置权限:

  • 在清单中声明大致/精确位置权限(只有在必要时才声明后台位置权限)。
  • 使用 Activity Result API 或 accompanist-permissions 在运行时请求权限。
  • 同时请求精确和大致位置权限,以支持精确/大致位置切换。
  • 显示请求理由,并将永久拒绝权限的用户引导至应用设置。
  • 将后台位置作为单独且有充分理由的两步流程处理。

至此,地图与位置服务部分就完成了——您可以显示地图、定位用户、放置标记,并以正确的方式请求权限。

常见问题解答

「正确处理位置权限」课时是免费的吗?

是的 — 「正确处理位置权限」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「正确处理位置权限」这节课中我会学到什么?

了解前台位置和精确位置 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「正确处理位置权限」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 显示地图
  2. 获取用户位置
  3. 标记与摄像机
  4. 正确处理位置权限
← 返回 Android Academy