Android 定位权限兼容问题详解 —— 新手指南

一、定位权限的核心变化与版本适配

Android 版本 定位权限关键变更
≤ 8.1 (API 27) 仅需声明 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 即可访问位置。
9.0 (API 28) 前台服务必须请求 FOREGROUND_SERVICE 权限(与定位无关,但影响后台定位服务)。
10.0 (API 29) 后台定位需显式声明 ACCESS_BACKGROUND_LOCATION,用户需手动授权。
11.0 (API 30) 动态请求后台定位时,用户可选择仅授予"使用时允许"(前台定位)。
12.0 (API 31) 引入 近似位置(ACCESS_COARSE_LOCATION)精确位置(ACCESS_FINE_LOCATION) 分离。

二、前台定位与后台定位的权限区别

权限类型 权限名称 作用场景
前台定位 ACCESS_COARSE_LOCATION 获取大致位置(基站/WiFi,精度约100米)。
ACCESS_FINE_LOCATION 获取精确位置(GPS,精度约10米)。
后台定位 ACCESS_BACKGROUND_LOCATION 应用在后台(不可见或未运行)时访问位置(需 Android 10+ 显式声明和用户授权)。

三、适配策略与代码示例

1. 权限声明(AndroidManifest.xml)

xml 复制代码
<!-- 始终声明前台定位权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Android 10+ 后台定位需显式声明 -->
<uses-permission
    android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
    android:maxSdkVersion="30" /> <!-- Android 11+ 后台权限通过动态请求 -->

2. 动态权限请求(适配不同版本)

kotlin 复制代码
// 检查并请求权限
fun requestLocationPermissions(activity: Activity) {
    val permissions = mutableListOf<String>()

    // Android 12+ 需要明确请求近似或精确位置
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
    } else {
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
    }

    // Android 10+ 需额外请求后台定位权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
    }

    ActivityCompat.requestPermissions(
        activity,
        permissions.toTypedArray(),
        REQUEST_CODE_LOCATION
    )
}

3. 处理权限结果

kotlin 复制代码
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String>,
    grantResults: IntArray
) {
    if (requestCode == REQUEST_CODE_LOCATION) {
        val isFineLocationGranted = grantResults.getOrNull(
            permissions.indexOfFirst { it == Manifest.permission.ACCESS_FINE_LOCATION }
        ) == PackageManager.PERMISSION_GRANTED

        val isBackgroundGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            grantResults.getOrNull(
                permissions.indexOfFirst { it == Manifest.permission.ACCESS_BACKGROUND_LOCATION }
            ) == PackageManager.PERMISSION_GRANTED
        } else {
            true // Android 9 及以下无需后台权限
        }

        if (isFineLocationGranted) {
            if (isBackgroundGranted) {
                // 前台和后台定位均授权
            } else {
                // 仅前台定位授权
            }
        } else {
            // 定位权限被拒绝
        }
    }
}

四、兼容性问题与解决方案

1. Android 12+ 的近似位置权限

  • 用户可能仅授予近似位置:需在代码中处理低精度场景。

  • 适配建议

    kotlin 复制代码
    val hasCoarse = ActivityCompat.checkSelfPermission(
    this, Manifest.permission.ACCESS_COARSE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED
    
    val hasFine = ActivityCompat.checkSelfPermission(
        this, Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED
    
    val locationPermissionLevel = when {
        hasFine -> "精确位置"
        hasCoarse -> "近似位置"
        else -> "无权限"
    }

2. 后台定位权限的显式引导(Android 10+)

  • 用户拒绝后台权限时 :引导用户到设置页手动开启。

    kotlin 复制代码
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
        !Environment.isBackgroundLocationPermissionGranted()) {
        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
            data = Uri.fromParts("package", packageName, null)
        }
        startActivity(intent)
    }

3. Android 11 的"仅在使用时允许"选项

  • 用户选择"仅在使用时允许":应用在后台无法获取位置。

  • 检测权限状态

    kotlin 复制代码
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        val locationPermissionStatus = ActivityCompat.checkSelfPermission(
            this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
        )
        val isBackgroundAccessAllowed = locationPermissionStatus == PackageManager.PERMISSION_GRANTED
    }

五、最佳实践与注意事项

  1. 最小化权限请求

    • 仅请求必要的精度(如导航应用需 ACCESS_FINE_LOCATION,天气应用可用 ACCESS_COARSE_LOCATION)。
  2. 用户引导与解释

    • 在请求权限前通过弹窗说明用途(使用 shouldShowRequestPermissionRationale 判断是否需要解释)。
  3. 后台定位的合理使用

    • 遵循 Google Play 政策,避免滥用后台定位(如仅用于导航、健身跟踪等核心功能)。
  4. 测试覆盖

    • 在不同 Android 版本(尤其 10、11、12)上测试权限逻辑。

六、示例代码(使用 AndroidX Activity Result API)

kotlin 复制代码
// 定义权限请求合约
val locationPermissionRequest = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    val fineGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
    val coarseGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false
    val backgroundGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        permissions[Manifest.permission.ACCESS_BACKGROUND_LOCATION] ?: false
    } else {
        true
    }

    when {
        fineGranted -> { /* 精确位置可用 */ }
        coarseGranted -> { /* 近似位置可用 */ }
        else -> { /* 无权限 */ }
    }
}

// 触发权限请求
fun requestPermissions() {
    val permissions = mutableListOf<String>()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
    } else {
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
    }
    locationPermissionRequest.launch(permissions.toTypedArray())
}

七、总结

  • 前台定位 :需 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION,适配 Android 12+ 的精度分离。
  • 后台定位 :Android 10+ 需显式请求 ACCESS_BACKGROUND_LOCATION,用户可能手动拒绝。
  • 兼容性关键:动态检查权限、处理不同精度场景、引导用户设置。
  • 用户隐私:遵循最小化原则,仅在必要时请求权限并提供清晰解释。

更多分享

  1. Android跨进程通信中的关键字详解:in、out、inout、oneway
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
  9. Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
  10. Kotlin Result 类型扩展详解 ------ 新手使用指南
相关推荐
studytosky1 小时前
OpenClaw 入门与 Skill 开发
linux·服务器·ai编程
stevenzqzq1 小时前
app性能日志工具
android
DB哥讲数据库1 小时前
【最新】MySQL9.7安装教程:图文详解(附MySQL安装包)
android·mysql·adb
名不经传的养虾人1 小时前
从0到1:企业级AI项目迭代日记 Vol.67|能运行,和能商用
人工智能·agent·ai编程·企业ai·多agent协作
iCOD3R2 小时前
Skill - 3秒生成精美GitHub主页
前端·程序员·ai编程
孤狼GPT2 小时前
Codex还是看不到GPT-5.6?CLI、App和模型入口排查
ai编程·codex·codex cli·gpt-5.6·chatgpt桌面版
IT乐手2 小时前
Android 写本地日志工具类
android·java
非典型Android程序员3 小时前
Launcher学习总结
android
TrisighT3 小时前
Agent 并行工具调用快了 0.8 秒——200 组实测发现,多花的那 3100 token 全打了水漂
aigc·agent·ai编程
Databend3 小时前
AST Visitor API 迁移,怎么证明 AI 没改坏 SQL 语义?
数据库·ai编程