Android Permissions and Compose State-Driven UI
约 319 字大约 1 分钟
Android
2025-12-23
1. Background
This was my first time working with native Android app development. The app I wanted to build needed camera access, and Android 6.0 (API 23) and above requires runtime permission requests for sensitive permissions such as camera, microphone, or storage.
In our project, we needed the CAMERA permission to display the rear CameraX preview inside a Compose app.
2. Declaring permissions in the Manifest
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" android:required="true"/>uses-permission: tells the system that the app needs the CAMERA permissionuses-feature: declares a dependency on camera hardware- Declaring a permission is not the same as having it granted
3. Runtime permission requests
Modern Android recommends the Activity Result API together with Compose state.
1. Register the permission launcher
private lateinit var cameraPermissionLauncher: ActivityResultLauncher<String>
cameraPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
hasCameraPermission.value = granted
}registerForActivityResultregisters the request and callbackActivityResultContracts.RequestPermissionis the built-in contract for a single permissiongranted: Booleantells you whether the user allowed it- Updating state triggers Compose recomposition automatically
2. Launch the permission request
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)4. Compose state-driven UI
val hasCameraPermission = mutableStateOf(
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
)true-> showCameraPreviewfalse-> show the permission request button
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
if (hasCameraPermission.value) {
CameraPreview(activity = this)
} else {
NoPermissionView(
onRequestPermission = {
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
}
)
}
}Compose state-driven UI responds to permission changes automatically.
5. NoPermissionView example
@Composable
fun NoPermissionView(onRequestPermission: () -> Unit) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Button(
onClick = onRequestPermission,
modifier = Modifier.width(200.dp).height(60.dp)
) {
Text("请求相机权限")
}
}
}6. What I learned
- Manifest declaration and runtime authorization are different things
ActivityResultLauncher + Compose stateis a clean pattern- State-driven UI removes the need for manual refresh logic
- Callbacks help keep asynchronous logic separate from rendering
