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

}
相关推荐
Deryck_德瑞克16 分钟前
【已解决】MySQL连接出错 1045 - Access denied for user ‘root‘@‘::1‘
android·mysql·adb
2501_9159184130 分钟前
iOS性能测试工具 Instruments、Keymob的使用方法 不局限 FPS
android·ios·小程序·https·uni-app·iphone·webview
.豆鲨包1 小时前
【Android】组件化搭建的一般流程
android
心有—林夕2 小时前
MySQL 误操作恢复完全指南
android·数据库·mysql
忙什么果2 小时前
Mamba学习笔记2:Mamba模型
android·笔记·学习
Wyawsl3 小时前
MySQL故障排查与优化
android·adb
私人珍藏库5 小时前
[Android] 后台视频录制 FadCam v3.0.1
android·app·工具·软件·多功能
Z_Wonderful5 小时前
在 **Next.js** 中使用 `mysql2` 连接 MySQL 数据库并查询 `xxx` 表的数据
android·数据库
FirstFrost --sy5 小时前
MySql 内外连接
android·数据库·mysql
激昂网络5 小时前
在Ubuntu 24.04上编译T527 Android系统:遇到的几个问题及解决方法
android·linux·ubuntu