Runtime Permission — 一种在应用程序运行时请求权限的机制,于 Android 6.0(API 23)中引入。与在安装时设置权限不同,runtime permissions 允许用户随时授予或撤销对敏感数据(相机、地理位置、联系人)的访问权限。根据 Android Developers (2026),Google Play 中超过 85% 的应用至少使用一个 runtime permission。
要点
Runtime Permission(运行时权限)是一种 Android 安全模型,应用在用户真正需要某项功能时才请求访问敏感数据。在 Android 6.0 之前,所有权限在安装应用时提供,用户无法在不完全卸载应用的情况下撤销权限。
在 Android 6.0 之前,用户在安装时会看到所有权限列表,可以选择全部同意或拒绝安装。2015 年的一项研究表明,87% 的用户在安装时不会阅读权限列表。Android 6.0 引入了 runtime permissions,将权限分为 normal(自动)和 dangerous(需要请求)。Android 11 增加了 one-time permissions — 应用关闭后自动撤销。Android 13 引入了 Photo Picker 并将推送通知作为独立的 runtime permissions。
iOS 从 iOS 10 开始使用类似的模型,在首次访问相机、麦克风和地理位置时请求权限。然而,iOS 没有「normal permissions」的概念 — 每个权限都明确请求,拒绝后保留,直到开发者通过系统设置再次请求。
Runtime Permission 通过系统对话框工作,由 requestPermissions() 方法(AndroidX 中为 ActivityResultLauncher)调用。系统显示带有解释的标准对话框,用户选择「允许」或「拒绝」。用户响应后,回调结果,应用处理用户的决定。
private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
startCamera()
} else {
showPermissionDeniedDialog()
}
}
}
private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED -> {
startCamera()
}
ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) -> {
showRationaleDialog { requestPermissionLauncher.launch(Manifest.permission.CAMERA) }
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
shouldShowRequestPermissionRationale 方法在用户已拒绝过一次请求时返回 true。在这种情况下,建议显示一个解释对话框,说明应用为何需要该权限,然后再次请求。这可将用户同意概率提高 30–40%(根据 Google I/O 2024 数据)。
Android 将所有权限分类为几个保护级别:normal、dangerous、signature 和 special。Normal permissions 在安装时自动授予。Dangerous permissions 需要运行时请求。Signature permissions 仅对使用相同证书签名的应用可用。
| 组 | 权限 | API Level |
|---|---|---|
| CAMERA | CAMERA | API 23+ |
| LOCATION | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION | API 23+ (background — API 29+) |
| STORAGE | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, READ_MEDIA_IMAGES (API 33+) | API 23+ (API 33 中的变更) |
| PHONE | READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG | API 23+ |
| MICROPHONE | RECORD_AUDIO | API 23+ |
| CONTACTS | READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS | API 23+ |
| NOTIFICATIONS | POST_NOTIFICATIONS | API 33+ |
Special permissions(SYSTEM_ALERT_WINDOW、WRITE_SETTINGS、MANAGE_EXTERNAL_STORAGE)需要通过 Settings.ACTION_MANAGE_OVERLAY_PERMISSION 额外跳转到系统设置。这些权限无法通过标准系统对话框请求,需要用户明确转到设置屏幕。
Android 12 引入了 runtime permissions 模型的重大变化。One-time permissions 允许仅在一次会话中授予对相机、麦克风或地理位置的访问权限。用户关闭应用后,权限自动撤销。Privacy indicators — 状态栏中的绿色指示器,显示应用何时使用相机或麦克风。
// Android 12+ — 处理一次性地理位置权限
private fun checkLocationPermission() {
val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val fineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION]
val coarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION]
if (fineLocationGranted == true) {
showUserLocation()
} else {
showLocationDisabledDialog()
}
}
permissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
// 检查权限是否被系统撤销(Android 12+)
class PermissionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_PERMISSION_REVOCATION) {
handleRevokedPermission(intent.getStringExtra(Intent.EXTRA_REVOKED_PERMISSION))
}
}
}
Android 13 将 POST_NOTIFICATIONS 权限添加到 dangerous 组,要求明确请求才能发送推送通知。Android 14 引入了后台地理位置限制:应用每次请求 background location 时必须获得用户的明确批准。Photo Picker(API 33+)取代了选择图片时对 READ_EXTERNAL_STORAGE 的需求。
用户拒绝授予权限是正常情况,需要正确处理。有两种类型的拒绝:一次性(用户点击了「拒绝」)和永久性(用户选择了「不再询问」)。在第二种情况下,系统对话框不再显示,应用必须将用户重定向到系统设置。
第一次拒绝后,应用应显示一个 rationale dialog — 自行解释为何需要该权限。如果用户再次拒绝,应通过 Settings.ACTION_APPLICATION_DETAILS_SETTINGS 将用户重定向到应用设置界面。Material 3 建议使用 PermissionRequestBottomSheet 以获得更自然的用户体验。
重要的是不要在拒绝时完全阻止应用功能。例如,如果用户拒绝了地理位置,应用应提供手动输入地址的方式。对于相机 — 允许从图库加载图片。Google 建议始终为所有 runtime permissions 提供备用机制。
Runtime permissions 不仅是技术机制,也是用户对应用信任的要素。在不合适的时机请求权限(例如,首次启动时)会显著降低同意概率。Google Play Store 分析权限请求的频率和上下文:激进请求的应用在搜索结果中排名较低。
上下文 — 在执行需要权限的操作之前立即请求。最小化 — 仅请求功能真正需要的权限。透明 — 在系统对话框之前向用户解释为何需要该权限。撤销 — 订阅 ACTION_PERMISSION_REVOCATION 以正确处理运行时的权限撤销。
要测试 runtime permissions,请使用 adb 命令:adb shell pm revoke <package> android.permission.CAMERA 可以在不重新安装应用的情况下模拟权限撤销。Espresso 和 UiAutomator 通过 GrantPermissionRule 支持测试权限对话框。将这些工具集成到 CI/CD 流水线中对于包含 runtime permissions 的应用是必需的。
Google Play Console 提供 Permission auditing(权限审计)部分,开发者可以查看权限请求的频率、提供访问权限的用户百分比以及哪些权限被撤销。分析这些数据有助于识别低效的请求并优化用户体验。例如,如果不到 40% 的用户提供地理位置,则需要重新考虑请求时机并添加更具说服力的 rationale。
使用 Android Vitals 监控与权限相关的 ANR(Application Not Responding)也至关重要。如果权限请求在主线程上执行或系统对话框阻塞了 UI,可能会在慢速设备上导致 ANR。将权限检查和请求移到单独的线程或使用 Kotlin 协程进行异步处理,以避免阻塞用户界面。
常见问题
shouldShowRequestPermissionRationale 在永久性拒绝时返回 false(当用户选择了「不再询问」时)。该方法在一次性拒绝时返回 true,允许显示 rationale 对话框。如果方法返回了 false,唯一的办法是将用户重定向到应用的系统设置。
可以,ActivityResultContracts.RequestMultiplePermissions 允许一次调用请求一组权限。系统将依次为每个权限显示对话框。建议将逻辑相关的权限分组(例如,CAMERA 和 RECORD_AUDIO 用于视频录制),但一次不要请求超过 2–3 个。
Android TV 使用相同的 runtime permissions 模型,对话框显示在电视屏幕上。Wear OS 3+ 版本支持 runtime permissions,但对话框显示在手表上。对于 Android Auto,所有权限在手机上请求,车载系统通过 bridge 连接获取已批准的权限。
根据初步信息,Android 16 将引入一次性权限的「权限过期」机制,24 小时后自动撤销。同时预计将收紧对 background location 的要求,并扩展 dangerous permissions 列表以涵盖新类别(环境传感器、Wi-Fi 扫描)。具体细节将在 2027 年 Q3 公布。
iOS 不支持「normal permissions」— 每个权限通过系统对话框明确请求。用户可以随时通过设置撤销权限。主要区别在于 iOS 不会通过类似 checkSelfPermission 的方式预先检查权限状态:系统在首次访问受保护 API 时自动显示对话框。
总结
POST_NOTIFICATIONS 添加为 runtime permission,Android 14 加强了对后台地理位置的要求。READ_EXTERNAL_STORAGE 的需求。我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。