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 {
        // 权限未被授予
    }

}
相关推荐
2501_915921432 小时前
iOS APP上架工具,在没有 Mac 的环境中发布苹果应用
android·macos·ios·小程序·uni-app·iphone·webview
范特西林2 小时前
第一篇:从电源键到上帝进程——硬件觉醒与 Init 的诞生
android
养了一只皮卡丘2 小时前
ubuntu22.04搭建mysql8.0.45 mgr (2)
android·adb
常利兵2 小时前
深入理解Android ViewModel&SavedStateHandle:告别数据丢失,打造稳健UI架构
android·ui·架构
范特西林2 小时前
第四篇:从点击到显示——App 启动与 Activity 生命周期全追踪
android
ke_csdn2 小时前
安卓的视频通讯
android·音视频
范特西林2 小时前
第二篇:Java 世界的“创世神”:Zygote 如何一秒孵化一个 App?
android
范特西林2 小时前
第三篇:SystemServer——Android 框架层的大脑
android
robotx2 小时前
aosp单编单刷framework模块以及恢复remount
android