从安装时的全盘托出,到运行时的小心翼翼,再到今天的精细化管控,Android权限的演变是一部用户隐私的「觉醒史」,也是开发者适配的「奋斗史」
https://via.placeholder.com/1200x600/0D47A1/FFFFFF?text=Android+Permission+Evolution+从安装时到运行时的革命
作为一名Android开发者,你是否曾经历过这样的困境:
-
应用在新系统发布后突然出现权限被拒的崩溃?
-
用户投诉「为什么这个应用要这么多权限?」?
-
面对
Scoped Storage、POST_NOTIFICATIONS等新概念感到困惑?
这一切都源于Android权限系统那场静默却深刻的革命。本文将用图文并茂的方式,带你完整回顾这段历程。

一、蛮荒时代:安装时权限的「霸王条款」
在Android 5.0 (API 21)及之前的世界,规则简单粗暴。
运作模式:
用户在安装界面点击「安装」,就必须接受应用要求的所有权限。这是一种「全部或一无所有」的交易。
https://via.placeholder.com/600x400/FF9800/FFFFFF?text=安装界面+必须接受所有权限才能安装
代码示例:
<!-- 在AndroidManifest.xml中声明即可获得权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CAMERA"/>
开发者视角:
-
优点:简单,只需在配置文件中声明
-
缺点:用户信任危机,手电筒应用要求读取联系人显然不合理
这个阶段是「一刀切」,效率优先但牺牲了用户选择权。
二、里程碑转折:Android 6.0的运行时权限
这是Android权限史上最重大的变革,标志着从「效率优先」转向「隐私优先」。
核心创新:
-
危险权限需动态申请
-
用户可随时撤销权限
https://via.placeholder.com/400x300/4CAF50/FFFFFF?text=运行时权限对话框+用户可以选择允许或拒绝
开发者适配挑战:
代码从「静态声明」变成「动态谈判」。
// 权限检查与申请流程
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
// 先解释为什么需要这个权限
showPermissionExplanation {
// 然后弹出系统对话框
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CODE_CAMERA
)
}
} else {
openCamera()
}
// 处理用户选择结果
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
REQUEST_CODE_CAMERA -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
// 优雅处理拒绝
showPermissionDeniedMessage()
}
}
}
}
这个阶段是「运行时谈判」,用编码成本换取用户信任。
三、持续收紧:从外部存储到后台行为
Google发现仅控制权限开关不够,还需要控制行为边界。
Android 8.0:安装未知应用权限
https://via.placeholder.com/400x300/2196F3/FFFFFF?text=未知应用授权+每个应用需要单独开启
背景:防止应用随意「静默安装」其他应用。
适配代码:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
// 检查是否有安装权限
if (packageManager.canRequestPackageInstalls()) {
installApk()
} else {
// 引导用户到设置页面开启
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = Uri.parse("package:$packageName")
}
startActivityForResult(intent, REQUEST_INSTALL_PERMISSION)
}
Android 10:分区存储革命
https://via.placeholder.com/600x400/009688/FFFFFF?text=分区存储+应用只能访问自己的沙箱和公共媒体
背景:解决应用获得存储权限后「偷窥」整个SD卡的问题。
代码对比:
// 旧方式:直接文件路径访问(已废弃)
val file = File(Environment.getExternalStorageDirectory(), "myfile.txt")
// 新方式:使用MediaStore API
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "image.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val uri = contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values
)
Android 10:后台位置访问限制
新增权限:ACCESS_BACKGROUND_LOCATION
将位置访问明确分为「前台」和「后台」。
四、精细化管控:权限的「微观管理」时代
系统对权限的管理达到前所未有的细致程度。
Android 11:一次授权 & 自动重置
https://via.placeholder.com/400x300/FF5722/FFFFFF?text=仅限这一次+单次授权选项
代码处理:
when {
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
// 有完整权限
}
shouldShowRequestPermissionRationale(permission) -> {
// 用户选择了「仅限这一次」,需要重新申请
showRationaleDialog()
}
else -> {
// 首次申请或被永久拒绝
requestPermissions(arrayOf(permission), requestCode)
}
}
Android 12:权限分离与大致位置
https://via.placeholder.com/400x300/673AB7/FFFFFF?text=位置精度选择+精确vs大致
蓝牙权限细化:
<!-- 旧的宽泛权限 -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- 新的细化权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Android 13:权限「拆箱」与通知权限
媒体权限三分天下:
<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"/>
运行时通知权限:
// 通知权限需要动态申请
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS),
REQUEST_CODE_NOTIFICATIONS)
}
}
五、前瞻与智能:AI驱动的隐私未来
Android 14:部分媒体访问
https://via.placeholder.com/400x300/795548/FFFFFF?text=照片选择器+用户选择特定媒体
使用照片选择器:
// 启动照片选择器,用户选择特定照片
val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
uri?.let { handleSelectedMedia(it) }
}
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
Android 15 & 16:预测性权限与健康数据
健康连接权限加强:
<!-- 需要声明使用健康数据的类型 -->
<uses-permission android:name="android.permission.BODY_SENSORS"/>
<uses-permission android:name="android.permission.HEALTH_DATA"/>
<!-- 在manifest中声明健康数据类型 -->
<meta-data android:name="health_permissions"
android:resource="@array/health_permissions"/>
权限演进时间线全景图
https://via.placeholder.com/800x400/303F9F/FFFFFF?text=Android+权限演进时间线+从一刀切到精装修
开发者适配Checklist
✅ 立即行动项
-
更新Target SDK到最新稳定版
-
实现完整的运行时权限流程
-
适配分区存储,放弃File API
🔄 近期优化项
-
请求最小必要权限
-
添加权限使用解释
-
处理边缘情况(一次授权、自动重置)
🎯 前瞻规划项
-
采用照片选择器
-
优化后台数据访问
-
关注预测性权限适配
实用代码模板
/**
* 完整的权限请求工具类
*/
class PermissionManager private constructor(private val activity: FragmentActivity) {
fun requestPermission(
permission: String,
rationale: String,
onGranted: () -> Unit,
onDenied: () -> Unit
) {
when {
// 已有权限
ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED -> {
onGranted()
}
// 需要解释
activity.shouldShowRequestPermissionRationale(permission) -> {
showRationaleDialog(rationale) {
doRequestPermission(permission, onGranted, onDenied)
}
}
// 直接请求
else -> {
doRequestPermission(permission, onGranted, onDenied)
}
}
}
private fun doRequestPermission(
permission: String,
onGranted: () -> Unit,
onDenied: () -> Unit
) {
activity.registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) onGranted() else onDenied()
}.launch(permission)
}
}
总结
Android权限的演进历程清晰地展示了:
用户控制权:无 → 有 → 强 → 精 → 智
作为开发者,顺应这个潮流不仅是合规要求,更是赢得用户信任的关键。在当今应用生态中,尊重用户隐私本身就是一种强大的核心竞争力。
一个在权限使用上克制、透明、优雅的应用,更容易获得用户的长期青睐。这场从「一刀切」到「精装修」的旅程还在继续,你,准备好了吗?

被折叠的 条评论
为什么被折叠?



