前言 在 Jetpack Compose 中,可以通过 Activity Result API 来同时同时申请多个权限。
我们可以利用 rememberLauncherForActivityResult 配合 ActivityResultContracts.RequestMultiplePermissions 来处理多个权限的请求,这是 Android 官方推荐的方式。
添加配置 AndroidManifest.xml
根节点下添加需要的权限
1 2 3 4 5 6 7 8 9 10 11 <uses-feature android:name ="android.hardware.camera" android:required ="false" /> <uses-permission android:name ="android.permission.CAMERA" /> <uses-permission android:name ="android.permission.INTERNET" /> <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name ="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name ="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name ="android.permission.ACCESS_COARSE_LOCATION" />
权限申请组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 import android.content.Contextimport android.content.Intentimport android.content.pm.PackageManagerimport android.net.Uriimport android.os.Buildimport android.provider.Settingsimport android.util.Logimport android.widget.Toastimport androidx.activity.ComponentActivityimport androidx.activity.compose.rememberLauncherForActivityResultimport androidx.activity.result.contract.ActivityResultContractsimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.heightimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.layout.widthimport androidx.compose.foundation.shape.RoundedCornerShapeimport androidx.compose.material3.Buttonimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.unit.dpimport androidx.compose.ui.unit.spimport androidx.core.content.ContextCompatfun hasPermanentlyDeniedPermissions ( context: Context , permissions: Array <String > ) : Boolean { return if (context is ComponentActivity) { permissions.any { permission -> !context.shouldShowRequestPermissionRationale(permission) } } else { false } } fun openAppSettings (context: Context ) { val intent = Intent( Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package" , context.packageName, null ) ) context.startActivity(intent) } @Composable fun NativePermissionComp () { val context = LocalContext.current val permissionList = mutableListOf( android.Manifest.permission.CAMERA, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION ) val isAndroid13OrHigher = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU if (!isAndroid13OrHigher) { permissionList.add(android.Manifest.permission.READ_EXTERNAL_STORAGE) } val permissions = permissionList.toTypedArray() var allPermissionsGranted by remember { mutableStateOf(false ) } allPermissionsGranted = permissions.all { permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED } val multiplePermissionsLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestMultiplePermissions() ) { permissionsMap -> allPermissionsGranted = permissionsMap.all { it.value } if (!allPermissionsGranted) { val result = hasPermanentlyDeniedPermissions(context, permissions) if (result) { openAppSettings(context) } } } when { allPermissionsGranted -> { Toast.makeText(context, "所有权限已授予,可以使用相关功能" , Toast.LENGTH_SHORT).show() } else -> { Box( modifier = Modifier .fillMaxSize() .background(Color(0x33000000 )) ) { Column( modifier = Modifier .width(300. dp) .background(Color.White, RoundedCornerShape(8. dp)) .padding(10. dp, 20. dp) .align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text("需要以下权限才能继续:\n相机、存储、位置" ) Button( modifier = Modifier .padding(top = 10. dp), onClick = { multiplePermissionsLauncher.launch(permissions) }) { Text("申请权限" , fontSize = 12. sp) } } } } } }
方法说明 方法1 shouldShowRequestPermissionRationale() 是 Android 权限系统中的一个关键方法,用于判断是否需要向用户解释为什么需要某个权限。
其返回值的含义如下:
true :
表示系统认为应该向用户解释申请该权限的原因
通常发生在:用户曾经一次拒绝 了该权限请求,但但未勾选 “不再询问” 选项
这是一个提示,说明你应该向用户解释为什么需要这个权限,以提高用户授权的可能性
false :
表示不需要解释权限申请原因,或无法再通过代码请求该权限
可能的情况包括:
用户从未拒绝过 该权限(首次请求)
用户拒绝时勾选了 “不再询问” (永久拒绝)
设备策略禁止该权限(如企业设备限制)
权限属于 “正常权限”(无需运行时申请的权限)
方法2 rememberLauncherForActivityResult 是 Jetpack Compose 中用于处理 Activity 结果回调的关键 API,它是对 Android 传统 startActivityForResult 机制的现代替代方案,专门为 Compose 声明式编程模型设计。
关键参数解析
contract(契约) :
定义了启动的目标和数据交换格式的协议
Android 提供了多种内置契约,例如:
ActivityResultContracts.RequestPermission():请求单个权限
ActivityResultContracts.RequestMultiplePermissions():请求多个权限
ActivityResultContracts.TakePicture():拍照
ActivityResultContracts.GetContent():选择文件
ActivityResultContracts.StartActivityForResult():通用的 Activity 启动
result callback(结果回调) :
当目标 Activity 完成并返回结果时触发
回调参数的类型由契约决定(例如权限请求返回 Boolean,文件选择返回 Uri?)
launch(input) :
触发启动器,参数类型由契约决定
例如权限请求需要传入权限字符串,文件选择不需要参数
注意 从Android 13及以后,您需要为READ_MEDIA_VIDEO、READ_MEDIA_IMAGES、READ_MEDIA_AUDIO设置权限,而不仅仅是请求READ_EXTERNAL_STORAGE将单独工作,甚至不会显示在设置中的权限页面中。
申请下面的这个权限,结果也会一直是false。
1 android.permission.READ_EXTERNAL_STORAGE