Jetpack Compose 的最佳处理运行时权限的方法
如果您的应用安装在运行Android 6.0(API级别23)或更高版本的设备上,则必须按照本指南中的步骤为用户请求运行时权限。
在Jetpack Compose中获取运行时权限有两种方法。
- 使用Activity Result
- 使用Accompanist Permissions库
接下来,让我们仔细研究上述两种方法,并附有示例。
使用Activity Result进行运行时权限
第一步是在manifest.xml文件中定义权限。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera"/>
在这个示例应用程序中,我使用相机权限示例来捕获图像。请查看我的其他示例,以了解如何使用相机捕获图像。
如何在 jetpack compose 中使用相机捕获图像 (howtodoandroid.com)
https://www.howtodoandroid.com/capture-image-in-jetpack-compose/
如何在 jetpack compose 中从图库选择图像 (howtodoandroid.com)
https://www.howtodoandroid.com/pick-image-from-gallery-jetpack-compose/
创建一个活动结果启动器来请求我们定义的权限。一旦启动,它将返回结果,无论该权限是否被授予。
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (it) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
cameraLauncher.launch(uri)
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
检查权限
在请求权限之前,我们需要检查权限是否已经被授予。如果已经授予,我们可以继续正常流程。如果权限未被授予,那么我们需要使用我们需要的权限来发起权限请求。
val permissionCheckResult = ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA)
if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) {
cameraLauncher.launch(uri)
} else {
permissionLauncher.launch(android.Manifest.permission.CAMERA)
}
最后,使用活动结果实现运行时权限的代码将如下所示:
val context = LocalContext.current
val file = context.createImageFile()
val uri = FileProvider.getUriForFile(
Objects.requireNonNull(context),
BuildConfig.APPLICATION_ID + ".provider", file
)
var capturedImageUri by remember {
mutableStateOf<Uri>(Uri.EMPTY)
}
val cameraLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) {
capturedImageUri = uri
}
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (it) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
cameraLauncher.launch(uri)
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(12.dp)) {
Button(onClick = {
val permissionCheckResult = ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA)
if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) {
cameraLauncher.launch(uri)
} else {
// Request a permission
permissionLauncher.launch(android.Manifest.permission.CAMERA)
}
}) {
Text(text = "Open Camera")
}
if (capturedImageUri.path?.isNotEmpty() == true) {
Image(
modifier = Modifier
.padding(16.dp, 8.dp)
.fillMaxWidth()
.size(400.dp),
painter = rememberImagePainter(capturedImageUri),
contentDescription = null
)
}
}
上面的效果如下:
请求多个权限
在某些情况下,我们需要一次请求多个权限,例如,位置权限。为此,我们需要使用不同的启动器方法和权限检查。我们来详细看看。
像往常一样,我们需要在 manifest.xml 文件中定义所需的权限。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
需要的第一步是创建包含我们要请求的权限列表。
val permissions = arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
使用ActivityResultContracts.RequestMultiplePermissions()
方法一次性请求多个权限。使用此函数并创建权限启动器。
val launcherMultiplePermissions = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissionsMap ->
val areGranted = permissionsMap.values.reduce { acc, next -> acc && next }
if (areGranted) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
现在,启动器已准备好进行多个权限。下一步是检查权限是否已经授予。如果没有授予,则使用权限列表启动权限启动器。
if(permissions.all {
ContextCompat.checkSelfPermission(
context,
it
) == PackageManager.PERMISSION_GRANTED
}) {
// Get the location
} else {
launcherMultiplePermissions.launch(permissions)
}
请求多个运行时权限的最终代码将是:
val permissions = arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
val launcherMultiplePermissions = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissionsMap ->
val areGranted = permissionsMap.values.reduce { acc, next -> acc && next }
if (areGranted) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(12.dp)) {
Button(onClick = {
if(permissions.all {
ContextCompat.checkSelfPermission(
context,
it
) == PackageManager.PERMISSION_GRANTED
}) {
// Get the location
} else {
launcherMultiplePermissions.launch(permissions)
}
}) {
Text(text = "Get Current Location")
}
}
使用accompanist permissions库
与第一种使用方法相比,使用accompanist permission库是一种更容易获得运行时权限的方法。在build.gradle文件中添加该库。
implementation "com.google.accompanist:accompanist-permissions:0.23.1"
添加依赖项后,需要使用rememberPermissionState()
方法来维护我们传递给它的权限状态。同时使用PermissionRequired()
来观察运行时的权限状态。它包含所有权限状态的视图,比如允许或拒绝的视图。
val permissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
PermissionRequired(
permissionState = permissionState,
permissionNotGrantedContent = {
Toast.makeText(context, "Permission not granted", Toast.LENGTH_SHORT).show()
},
permissionNotAvailableContent = {
Toast.makeText(context, "Permission not available", Toast.LENGTH_SHORT).show()
}) {
if (isShowCamera) {
cameraLauncher.launch(uri)
isShowCamera = false
}
}
由于在这个示例中使用相机,需要创建相机启动器来从活动结果中捕获图像。
val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) {
isShowCamera = false
capturedImageUri = uri
}
现在我们已经完成了权限状态和相机启动器的设置。下一步是使用权限。点击任何按钮或操作时,我们需要调用permissionState.hasPermission
来检查权限是否被授予。 如果没有被授予,我们需要调用权限状态启动器以显示运行时权限对话框。
Button(onClick = {
if(permissionState.hasPermission) {
cameraLauncher.launch(uri)
} else {
isShowCamera = true
permissionState.launchPermissionRequest()
}
}) {
Text(text = "Open Camera")
}
同样地,您可以使用Accompanist库请求多个权限。查看以下代码以获取使用Accompanist请求多个权限的示例。
val permissionsState = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
PermissionsRequired(
multiplePermissionsState = permissionsState,
permissionsNotGrantedContent = {
Toast.makeText(context, "Permission not granted", Toast.LENGTH_SHORT).show()
},
permissionsNotAvailableContent = {
Toast.makeText(context, "Permission not available", Toast.LENGTH_SHORT).show()
}) {
//content
}
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(12.dp)) {
Button(onClick = {
if(permissionsState.permissions.all {
ContextCompat.checkSelfPermission(
context,
it.permission
) == PackageManager.PERMISSION_GRANTED
}) {
// access location
} else {
permissionsState.launchMultiplePermissionRequest()
}
}) {
Text(text = "Location Permission")
}
}
效果如下
完整代码地址
https://github.com/velmurugan-murugesan/JetpackCompose/tree/master/RuntimePermissionJetpackCompose