Skip to Content

Permissions trong Android

1. Giới thiệu

Permissions bảo vệ quyền riêng tư của user bằng cách yêu cầu app xin phép trước khi truy cập sensitive data hoặc hardware.

2. Loại Permissions

Normal Permissions

Tự động được cấp, chỉ cần khai báo trong Manifest:

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Dangerous Permissions

Cần runtime request:

  • CAMERA
  • LOCATION
  • MICROPHONE
  • CONTACTS
  • CALENDAR
  • STORAGE
  • PHONE

3. Khai báo trong Manifest

<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Storage permissions --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

4. Request Permission - Activity

class MainActivity : ComponentActivity() { private val requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted -> if (isGranted) { openCamera() } else { showPermissionDeniedMessage() } } private fun checkCameraPermission() { when { checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> { openCamera() } shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> { showRationaleDialog() } else -> { requestPermissionLauncher.launch(Manifest.permission.CAMERA) } } } }

5. Multiple Permissions

private val requestMultiplePermissions = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> val allGranted = permissions.entries.all { it.value } if (allGranted) { startLocationTracking() } else { // Check which permissions were denied permissions.forEach { (permission, granted) -> if (!granted) { handleDenied(permission) } } } } private fun requestLocationPermissions() { requestMultiplePermissions.launch( arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) ) }

6. Compose với Accompanist

// build.gradle.kts implementation("com.google.accompanist:accompanist-permissions:0.34.0")

Single Permission

@OptIn(ExperimentalPermissionsApi::class) @Composable fun CameraScreen() { val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA) when { cameraPermissionState.status.isGranted -> { CameraPreview() } cameraPermissionState.status.shouldShowRationale -> { Column { Text("Camera permission is required for this feature") Button(onClick = { cameraPermissionState.launchPermissionRequest() }) { Text("Grant Permission") } } } else -> { Button(onClick = { cameraPermissionState.launchPermissionRequest() }) { Text("Request Camera Permission") } } } }

Multiple Permissions

@OptIn(ExperimentalPermissionsApi::class) @Composable fun LocationScreen() { val locationPermissions = rememberMultiplePermissionsState( listOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) ) when { locationPermissions.allPermissionsGranted -> { LocationContent() } locationPermissions.shouldShowRationale -> { RationaleDialog(onConfirm = { locationPermissions.launchMultiplePermissionRequest() }) } else -> { Button(onClick = { locationPermissions.launchMultiplePermissionRequest() }) { Text("Request Location Permissions") } } } }

7. Xử lý “Don’t ask again”

if (!shouldShowRequestPermissionRationale(permission) && checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // User selected "Don't ask again" showSettingsDialog() } private fun showSettingsDialog() { AlertDialog.Builder(this) .setTitle("Permission Required") .setMessage("Please enable permission in Settings") .setPositiveButton("Settings") { _, _ -> val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = Uri.fromParts("package", packageName, null) startActivity(intent) } .setNegativeButton("Cancel", null) .show() }

8. Permission Groups (Android 14+)

// Photo picker - không cần permission val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> uri?.let { handleSelectedImage(it) } } pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))

9. Best Practices

// ✅ Good: Request khi cần fun onCameraButtonClick() { requestCameraPermission() } // ❌ Bad: Request khi app start override fun onCreate() { requestAllPermissions() // Don't do this } // ✅ Good: Explain why @Composable fun PermissionRationale() { Column { Text("We need camera access to take photos for your profile") Button(onClick = { requestPermission() }) { Text("Continue") } } } // ✅ Good: Graceful degradation if (hasLocationPermission) { showNearbyPlaces() } else { showAllPlaces() // Fallback without location }

10. Special Permissions

// Overlay permission if (!Settings.canDrawOverlays(this)) { val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) startActivity(intent) } // Notification permission (Android 13+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }

📝 Tóm tắt

TypeRequest
NormalManifest only
DangerousRuntime request
SpecialIntent to Settings
Best PracticeDescription
Request in contextKhi user cần feature
Explain whyShow rationale
Handle denialGraceful degradation
Don’t ask againLink to Settings
Last updated on