跳转至

权限

配置

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-permissions:<version>"
}

Maven Central

单个权限

例如,我们需要获取相机权限,通过rememberPermissionState(Manifest.permission.CAMERA)获取到 PermissionState,然在布局时使用PermissionRequired可以很方便的进行布局,同时在权限被拒绝、永久拒绝或者允许时都有对应的lambda,通过调用permissionState.launchPermissionRequest()来申请权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="icu.bughub.app.basicssample">
    <uses-permission android:name="android.permission.CAMERA"/>

    <!-- 其他配置 -->
</manifest>
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionSample() {
    val permissionState = rememberPermissionState(
        permission = Manifest.permission.CAMERA
    )

    Scaffold(topBar = {
        TopAppBar(title = { Text("Permission Demo") })
    }) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {

            when (permissionState.status) {
                PermissionStatus.Granted -> {
                    Text("已经同意了相机权限")
                }
                //权限拒绝
                is PermissionStatus.Denied -> {
                    Column {
                        val text = if (permissionState.status.shouldShowRationale) {
                            //已经点击获取权限,此时拒绝
                            "相机权限已拒绝,点击按钮再次请求"
                        } else {
                            //默认情况下的拒绝
                            "相机权限已被禁止"
                        }
                        Text(text = text)
                        Button(onClick = {
                            permissionState.launchPermissionRequest()
                        }) {
                            Text("点击获取权限")
                        }
                    }
                }

            }

        }
    }

}

Result

左侧为谷歌新儿子Pixel2,右侧为某国产手机

可以看到有些许区别

  • 在Pixel2上,权限弹窗有3个操作:仅使用期间允许、仅限这一次、拒绝
  • 在Pixel2上,第一次拒绝后,状态变为 Not Granted 再次拒绝后才会变为Not Available,而某国产手机只有一次机会

多个权限

例如,我们想同时获取相机和录音权限,通过val permissionsState = rememberMultiplePermissionsState(permissions = listOf(Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO))获取到 PermissionsState, 通过调用permissionsState.launchMultiplePermissionRequest()来请求权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="icu.bughub.app.basicssample">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    <!-- 其他配置 -->

</manifest>
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionSample1() {
    val permissionsState = rememberMultiplePermissionsState(
        permissions =
        listOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
    )

    Scaffold(topBar = {
        TopAppBar(title = { Text("Permissions Demo") })
    }) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {

            permissionsState.permissions.forEach { permissionState ->

                when (permissionState.permission) {
                    Manifest.permission.CAMERA -> {
                        when (permissionState.status) {
                            PermissionStatus.Granted -> {
                                Text("已经同意了相机权限")
                            }
                            //权限拒绝
                            is PermissionStatus.Denied -> {
                                Column {
                                    val text = if (permissionState.status.shouldShowRationale) {
                                        //已经点击获取权限,此时拒绝
                                        "相机权限已拒绝,点击按钮再次请求"
                                    } else {
                                        //默认情况下的拒绝
                                        "相机权限已被禁止"
                                    }
                                    Text(text = text)
                                }
                            }

                        }
                    }

                    Manifest.permission.RECORD_AUDIO -> {
                        when (permissionState.status) {
                            PermissionStatus.Granted -> {
                                Text("已经同意了录音权限")
                            }
                            //权限拒绝
                            is PermissionStatus.Denied -> {
                                Column {
                                    val text = if (permissionState.status.shouldShowRationale) {
                                        //已经点击获取权限,此时拒绝
                                        "录音权限已拒绝,点击按钮再次请求"
                                    } else {
                                        //默认情况下的拒绝
                                        "录音权限已被禁止"
                                    }
                                    Text(text = text)
                                }
                            }

                        }
                    }
                }
            }

            Button(onClick = {
                permissionsState.launchMultiplePermissionRequest()
            }) {
                Text("点击获取权限")
            }

        }
    }

}

Result

左侧为谷歌新儿子Pixel2,右侧为某国产手机

你一定发现了多个和单个权限写法的不同,其实多个权限系统也提供了PermissionsRequired,但这个只能判断所有权限的状态是否允许或拒绝。

后台权限

例如,定位权限在 Android 10 以后就被拆分为前台权限Manifest.permission.ACCESS_FINE_LOCATION和后台权限Manifest.permission.ACCESS_BACKGROUND_LOCATION,且在 Android 11 后两个权限不能同时申请,建议增量式申请,也就是说要先请求前台权限之后才能申请后台权限

Permission

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="icu.bughub.app.basicssample">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

    <!-- 基本配置 -->

</manifest>
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionSample2() {
    val permissionState =
        rememberPermissionState(permission = Manifest.permission.ACCESS_FINE_LOCATION)

    val backgroundPermissionState =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            rememberPermissionState(permission = Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        } else {
            TODO("VERSION.SDK_INT < Q")
        }

    Scaffold(topBar = {
        TopAppBar(title = { Text("Permissions Demo") })
    }) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {

            when (permissionState.status) {
                PermissionStatus.Granted -> {
                    Button(onClick = {
                        backgroundPermissionState.launchPermissionRequest()
                    }) {
                        Text("前台定位权限已同意,点击获取后台定位权限")
                    }
                }
                //权限拒绝
                is PermissionStatus.Denied -> {
                    Column {
                        val text = if (permissionState.status.shouldShowRationale) {
                            //已经点击获取权限,此时拒绝
                            "前台定位权限已拒绝,点击按钮再次请求"
                        } else {
                            //默认情况下的拒绝
                            "前台定位权限已被禁止"
                        }
                        Text(text = text)
                    }
                }
            }

            when(backgroundPermissionState.status){
                PermissionStatus.Granted ->{
                    Text(text = "后台定位权限已同意")
                }

                //权限拒绝
                is PermissionStatus.Denied -> {
                    Column {
                        val text = if (permissionState.status.shouldShowRationale) {
                            //已经点击获取权限,此时拒绝
                            "后台定位权限已拒绝,点击按钮再次请求"
                        } else {
                            //默认情况下的拒绝
                            "后台定位权限已被禁止"
                        }
                        Text(text = text)
                    }
                }
            }

            Button(onClick = {
                permissionState.launchPermissionRequest()
            }) {
                Text("点击获取权限")
            }

        }
    }

}

如果两个权限同时申请,在 Android 11+ 上就会报错:

Error

Apps targeting 30 must have foreground permission before requesting background and must request background on its own.

视频教程