以申请位置权限为例。
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_LOCATION和Manifest.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 中)
申请权限步骤:
- 在 AndroidManifest.xml 中声明权限;
- 在 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 函数集成。
申请权限步骤:
- 在 AndroidManifest.xml 中声明权限;
- 在 Composable 函数中,使用
rememberLauncherForActivityResult方法记住一个权限请求的启动器; - 检查权限状态,如果未授予,则使用启动器请求权限;
- 在回调中处理权限授予或拒绝的情况;
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 {
// 权限未被授予
}
}