- M 6.0
- O 8.0
M 6.0
Android 6.0 版本(Api 23)推出了 动态权限管理。
应用权限简介
Android
应用默认情况下不拥有任何权限,申请权限要在AndroidManifest.xml
中静态声明。
若未在manifest
中声明权限,则在调用相应功能时,将抛出异常,一般不会catch
该异常,程序会直接崩溃:
Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
在** Android 6.0 之前, 所有的权限都在安装应用时显示给用户,选择安装则表示授权全部权限,之后无法取消授权。
在 Android 6.0 **之后, 危险权限(dangerous)
需在程序运行时显式弹框,请求用户授权。何时弹框由应用程序决定。
对于普通权限(normal)
,仍保持原声明方式,在安装应用程序时予以授权。
保护等级
permission
的保护等级通过protectionLevel
属性设置,共4种:
- normal
- dangerous
- signature
- signatureOrSystem
详见:http://developer.android.com/guide/topics/manifest/permission-element.html
签名相关的不常用, 剩下normal
和dangerous
官网 Guides: https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
总结下来:所有的权限仍在manifest
中静态声明,normal权限
在安装时自动授权,dangerous权限
需应用明确地请求用户授权。
对于Android 6.0
以下的手机,或以前开发的旧应用,dangerous权限
也是安装时授权。
normal 类权限
当安装或更新时,系统将授予应用请求的属于 PROTECTION_NORMAL
的所有权限。只需在AndroidManifest.xml
中声明,不必每次使用都检查权限,且用户不能取消以上授权。
序号 | 权限 |
---|---|
1 | android.permission.ACCESS_LOCATION_EXTRA_COMMANDS |
2 | android.permission.ACCESS_NETWORK_STATE |
3 | android.permission.ACCESS_NOTIFICATION_POLICY |
4 | android.permission.ACCESS_WIFI_STATE |
5 | android.permission.ACCESS_WIMAX_STATE |
6 | android.permission.BLUETOOTH |
7 | android.permission.BLUETOOTH_ADMIN |
8 | android.permission.BROADCAST_STICKY |
9 | android.permission.CHANGE_NETWORK_STATE |
10 | android.permission.CHANGE_WIFI_MULTICAST_STATE |
11 | android.permission.CHANGE_WIFI_STATE |
12 | android.permission.CHANGE_WIMAX_STATE |
13 | android.permission.DISABLE_KEYGUARD |
14 | android.permission.EXPAND_STATUS_BAR |
15 | android.permission.FLASHLIGHT |
16 | android.permission.GET_ACCOUNTS |
17 | android.permission.GET_PACKAGE_SIZE |
18 | android.permission.INTERNET |
19 | android.permission.KILL_BACKGROUND_PROCESSES |
20 | android.permission.MODIFY_AUDIO_SETTINGS |
21 | android.permission.NFC |
22 | android.permission.READ_SYNC_SETTINGS |
23 | android.permission.READ_SYNC_STATS |
24 | android.permission.RECEIVE_BOOT_COMPLETED |
25 | android.permission.REORDER_TASKS |
26 | android.permission.REQUEST_INSTALL_PACKAGES |
27 | android.permission.SET_TIME_ZONE |
28 | android.permission.SET_WALLPAPER |
29 | android.permission.SET_WALLPAPER_HINTS |
30 | android.permission.SUBSCRIBED_FEEDS_READ |
31 | android.permission.TRANSMIT_IR |
32 | android.permission.USE_FINGERPRINT |
33 | android.permission.VIBRATE |
34 | android.permission.WAKE_LOCK |
35 | android.permission.WRITE_SYNC_SETTINGS |
36 | com.android.alarm.permission.SET_ALARM |
37 | com.android.launcher.permission.INSTALL_SHORTCUT |
38 | com.android.launcher.permission.UNINSTALL_SHORTCUT |
权限组
新的权限模型提出了权限组的概念,即:同权限组内的某个权限被授权了,则该组中剩余的权限也会被自动获取授权。例:Android.permission-group.CALENDAR
权限组中的android.permission.WRITE_CALENDAR
权限被授权,则应用会自动获取android.permission.READ_CALENDAR
权限。
序号 | 名称 | 权限组 | 权限 |
---|---|---|---|
1 | 日历 | android.permission-group.CALENDAR |
android.permission.READ_CALENDAR android.permission.WRITE_CALENDAR |
2 | 摄像头 | android.permission-group.CAMERA |
android.permission.CAMERA |
3 | 通讯录 | android.permission-group.CONTACTS |
android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS |
4 | 地理位置 | android.permission-group.LOCATION |
android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION |
5 | 麦克风 | android.permission-group.MICROPHONE |
android.permission.RECORD_AUDIO |
6 | 电话 | android.permission-group.PHONE |
android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
7 | 传感器 | android.permission-group.SENSORS |
android.permission.BODY_SENSORS |
8 | 短信 | android.permission-group.SMS |
android.permission.SEND_SMS android.permission.RECEIVE_SMS android.permission.READ_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECEIVE_MMS android.permission.READ_CELL_BROADCASTS |
9 | 存储空间 | android.permission-group.STORAGE |
android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE |
动态权限使用
一
判断是否有权限:checkSelfPermission()
二
如果没有权限,弹窗给用户选择:requestPermission()
,第二个参数code
与onRequestPermissionResult()
方法中的code
对应。
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
三
弹出权限选择对话框前弹出提示,用于引导用户选择:shouldShowRequestPermissionRationale()
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity.this, Manifest.permission.CAMERA)) {
Snackbar.make(view, "请允许应用使用照相机", Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
}
四
判断用户是否已确认权限:onRequestPermissionResult()
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA:
if (PermissionUtil.verifyPermissions(grantResults)) {
Snackbar.make(mLayout, "同意授权", Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(mLayout, "拒绝授权", Snackbar.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
五
若用户在选择权限对话框时拒绝了某个权限的申请,那么再次申请该权限时会多出一个“不再询问”的checkbox
,若勾选,则程序再调用requestPermission()
,对话框也不会弹出。
六
同时处理多个权限,方案待定
兼容问题
一. 动态权限检查需在android 6.0
版本以上运行,即api 23
之前不能运行:
- 判断
SDK
版本号
public static boolean checkSDKVersion_23() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
- 使用
v4
兼容库,兼容所有版本
ContextCompat.checkSelfPermission()
授权返回PERMISSION_GRANTED
,否则返回PERMISSION_DENIED
,所有版本都是如此。ActivityCompat.requestPermissions()
在M
之前版本调用,OnRequestPermissionsResultCallback
直接被调用,带着正确的PERMISSION_GRANTED
或PERMISSION_DENIED
。ActivityCompat.shouldShowRequestPermissionRationale()
在M
之前版本调用,永远返回false
。
二. 在Fragment
中使用,用v13
兼容包,效果一样:
FragmentCompat.requestPermissions()
FragmentCompat.shouldShowRequestPermissionRationale()
-
Fragment
中嵌套Fragment
,子Fragment
中建议用getParentFragment().requestPermissions
方法回调父Fragment
中的onRequestPermissionsResult
,添加以下代码将回调透传到子Fragment
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
List fragmentList = getChildFragmentManager().getFragments();
if (fragmentList != null && fragmentList.size > 0) {
for (Fragment fragment : fragmentList ) {
if (fragment != null) {
fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
}
}
}
开源
PermissionsDispatcher 使用标注方式暂不支持嵌套Fragment
RxPermissions 基于RxJava
android-RuntimePermissions 谷歌官方示例
参考
Android M 动态权限获取
O 8.0
官方:
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。
对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE
。应用请求 READ_EXTERNAL_STORAGE
,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE
,因为该权限也属于同一 STORAGE
权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE
;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE
,则系统会立即授予该权限,而不会提示用户。
8.0 变更