Android 中的权限申请

以申请位置权限为例。

Android 中的位置权限

  • Manifest.permission.ACCESS_FINE_LOCATION:精确位置权限,允许应用访问精确的位置信息;
    • 精度:GPS、WiFi、基站等,精度可达数十米;
    • 耗电量:较高(特别是使用 GPS 时);
    • 使用场景:导航应用、运动轨迹记录、精确位置服务、基于位置的提醒等;
  • Manifest.permission.ACCESS_COARSR_LOCATION:粗略位置权限,允许应用访问大致的位置信息;
    • 精度:主要通过 WiFi 和基站,精度约为 100 ~ 300 米;
    • 耗电量:较低;
    • 使用场景:天气应用(城市级别定位)、本地新闻、附近商家推荐等;

通常,如果应用需要获取精确位置,则请求 ACCESS_FINE_LOCATION,但有些应用可能只需要大致位置,那么可以只请求 ACCESS_COARSR_LOCATION

版本限制:

  • 在 Android 6.0(API 级别 23)及以上版本,这些权限属于危险权限,需要在运行时向用户申请。因此,应该需要在代码中检查是否已经拥有这些权限,如果没有,则弹出对话框请求用户授权;
  • 在 Android 10(API 级别 29)及以上版本,如果应用在后台访问位置信息,还需要申请 Manifest.permission.ACCESS_BACKGROUND_LOCATION。但是,后台位置权限的申请有更严格的要求;
  • 在 Android 11(API 级别 30)及以上版本,系统会鼓励用户授权应用大致位置而不是精确位置。因此,在请求位置权限时,应用需要清楚的说明为什么需要这些权限;
  • 在 Android 12(API 级别 31)及以上版本,系统引入了 Manifest.permission.ACCESS_FINE_LOCATIONManifest.permission.ACCESS_COARSR_LOCATION 的分离。但在此前的版本中 ,Manifest.permission.ACCESS_FINE_LOCATION 包含了 Manifest.permission.ACCESS_COARSR_LOCATION

在 AndroidManifest.xml 中声明:

xml 复制代码
<!-- app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
    <!-- 位置权限声明 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <!-- 如果目标 API 31+ (Android 12),还需要声明 -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
    <application>
        <!-- 应用配置 -->
    </application>
</manifest>

1 传统的权限申请(在 Activity/Fragment 中)

申请权限步骤:

  1. 在 AndroidManifest.xml 中声明权限;
  2. 在 Activity 中检查权限是否已经授予。如果未授予,则请求权限,重写 onRequestPermissionResult 方法,处理权限请求结果;
kotlin 复制代码
private val locationPermissions = arrayOf(
    android.Manifest.permission.ACCESS_FINE_LOCATION,
    android.Manifest.permission.ACCESS_COARSE_LOCATION
)

private val PERMISSION_REQUEST_CODE = 100

private fun checkLocationPermissions() {
    if (ContextCompat.checkSelfPermission(
            this,
            android.Manifest.permission.ACCESS_FINE_LOCATION
        )
        != PackageManager.PERMISSION_GRANTED
    ) {
        // 权限还未授予,需要申请
        ActivityCompat.requestPermissions(this, locationPermissions, PERMISSION_REQUEST_CODE)
    } else {
        // 权限已经授予
    }
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    when (requestCode) {
        PERMISSION_REQUEST_CODE -> {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予
            } else {
                // 权限被拒绝
            }
        }
    }
}

2 在 Compose 中申请权限

在 Compose 中,我们使用 rememberLauncherForActivityResult 方法来启动一个权限请求合约(Contract),并在回调中处理结果。这种方式更加符合 Compose 的声明风格,并且可以方便地与 Composable 函数集成。

申请权限步骤:

  1. 在 AndroidManifest.xml 中声明权限;
  2. 在 Composable 函数中,使用 rememberLauncherForActivityResult 方法记住一个权限请求的启动器;
  3. 检查权限状态,如果未授予,则使用启动器请求权限;
  4. 在回调中处理权限授予或拒绝的情况;
kotlin 复制代码
@Composable
fun LocationPermissionRequest() {
    val context = LocalContext.current // Compose 中获取当前上下文(与生命周期绑定)

    // 使用 remember 缓存权限数组
    val locationPermissions = remember {
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,   // 精确位置
            Manifest.permission.ACCESS_COARSE_LOCATION  // 粗略位置
        )
    }

    // 使用 remember 缓存权限状态,是否已获取所有位置权限
    var hasLocationPermission by remember { mutableStateOf(false) }

    val locationPermissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        // 权限处理结果
        val findGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
        val coarseGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false

        hasLocationPermission = findGranted || coarseGranted
    }

    LaunchedEffect(Unit) {
        val currentPermissionState = locationPermissions.any { permission ->
            ContextCompat.checkSelfPermission(context, permission) ==
                    PackageManager.PERMISSION_GRANTED
        }
        hasLocationPermission = currentPermissionState

        if (!hasLocationPermission) {
            locationPermissionLauncher.launch(locationPermissions)
        }
    }

    if (hasLocationPermission) {
        // 权限被授予
    } else {
        // 权限未被授予
    }

}
相关推荐
Zhu75815 分钟前
在k8s集群部署MySQL单实例,支持多个主流发行版
android·mysql·kubernetes
AFinalStone3 小时前
Android 7系统异常问题排查(四)Framework层(上)—System Server Watchdog
android·系统异常
宝杰X75 小时前
Android Room3 多平台数据库
android·数据库
huibin1478523698 小时前
热缓解学习记录
android·学习
小田的博客11 小时前
SAP MM 供应商银行主数据更新报错!message R1228!
android·java·服务器
一笑的小酒馆12 小时前
Android智能猫砂盆视频加载慢卡顿问题分析
android
只会cv的小前端13 小时前
七巧低代码服务端脚本使用方法
android·低代码·rxjava
13 小时前
Android 自定义 View 实战:从零打造工业级全向摇杆控件(OmniJoystickView)
android·kotlin·自定义view·摇杆控件
雨白13 小时前
深入理解 Kotlin 协程 (十一):以逸待劳,探秘 select 多路复用与并发安全策略
android·kotlin
hunterandroid16 小时前
[Android 从零到一] Android 深度链接与 App Links:从 URI Scheme 到可验证的应用跳转
android