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.currentval 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 permissionpermissionLauncher.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 = falsecapturedImageUri = uri}
现在我们已经完成了权限状态和相机启动器的设置。下一步是使用权限。点击任何按钮或操作时,我们需要调用permissionState.hasPermission
来检查权限是否被授予。 如果没有被授予,我们需要调用权限状态启动器以显示运行时权限对话框。
Button(onClick = {if(permissionState.hasPermission) {cameraLauncher.launch(uri)} else {isShowCamera = truepermissionState.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