Android Runtime Permission API — Complete Guide
In this tutorial, you'll learn about Android Runtime Permission API. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
You're using the deprecated requestPermissions() and onRequestPermissionsResult() approach, and your code is verbose and error-prone.
Wrong Approach ❌
// Old deprecated approach
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE && grantResults.isNotEmpty() && grantResults[0] == GRANTED) {
getLocation()
}
}
Output: Deprecated code, manual request code handling, easy to mis-match.
Right Approach ✅
class MainActivity : AppCompatActivity() {
// Single permission launcher
private val cameraLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openCamera()
} else {
showDeniedMessage()
}
}
// Multiple permissions launcher
private val permissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val allGranted = permissions.values.all { it }
if (allGranted) {
startFeature()
} else {
permissions.entries.forEach { (permission, granted) ->
if (!granted && !shouldShowRequestPermissionRationale(permission)) {
// "Never ask again" was checked
showSettingsDialog()
}
}
}
}
// In Compose
@Composable
fun PermissionAwareCamera() {
val permissionState = rememberPermissionState(Manifest.permission.CAMERA)
Button(onClick = { permissionState.launchPermissionRequest() }) {
Text(if (permissionState.status.isGranted) "Camera Ready" else "Request Camera")
}
}
private fun requestPermissions() {
permissionsLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.CAMERA
)
)
}
}
Output: Clean, modern permission API with no request codes.
Prevention
- Use
registerForActivityResultwithRequestPermission/RequestMultiplePermissions. - Use Accompanist
rememberPermissionStatein Compose. - Check
shouldShowRequestPermissionRationaleafter denial in the result callback. - Request only permissions your app currently needs.
Common Mistakes with permission runtime
- Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro