Android6.0权限管理
2016-12-03 11:36
87 查看
运行时权限(Runtime permission)
android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。这是极其危险的事情
所以,在Android M 权限请求设计改版了,有点类似iOS的权限请求
1461651981945.jpg
在android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。
注意权限询问对话框不会自己弹出来。开发者不得不自己调用。如果开发者要调用的一些函数需要某权限而用户又拒绝授权的话,函数将抛出异常甚至导致程序崩溃.
旧版兼容
为了与旧版本兼容,比如你的build.gradle 中的
targetSdkVersion 设置为
23 之前,比如22.
也能在Android6.0 的手机上面跑,并且权限请求机制使用6.0之前的
安装时请求 的模式.
吐槽一下: Excuse Me? 这到底是兼容还是漏洞... targetSdkVersion 设置为23以前,不让跑6.0不是更合理?
可能处于市场应用的API版本考虑,不兼容估计大部分应用都不能跑6.0
所以,如果你觉得运行时弹出权限框让用户勾选很不友好,那么就取巧使用targetSdkVersion <23 吧,但这绝对不是长久之计...(丑陋...)
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 8
targetSdkVersion22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
1461662965441.jpg
6.0权限弹框的两种模式
1.初次请求,弹出对话框叫你勾选1461654947945.jpg
2,第二次请求之后,弹出对话框,出现不再提醒字样
1461655014689.jpg
当用户点击了不再提醒,你再次请求权限的时候,就不会弹出对话框,所以这时,你需要根据需求另做处理
下面会如何处理
6.0之后的权限分类
分为两类Normal permissions 和
Dangerous permissions
Normal permissions(普通权限)
只需要在xml中申请就可以了,与6.0之前没什么区别包括的权限有
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
其实不需要记,记住哪些是危险权限就是了
Dangerous permissions(危险权限)
危险权限,需要在运行时请求.注意: 危险权限是按组来分的,所以,当你申请了多个同组的危险权限时,运行时只需要申请一个就行
例如:
<!-- 电话 -->
<uses-permissionandroid:name="android.permission.READ_CALL_LOG"
/>
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"
/>
<uses-permissionandroid:name="android.permission.CALL_PHONE"
/>
<uses-permissionandroid:name="android.permission.WRITE_CALL_LOG"
/>
<uses-permissionandroid:name="android.permission.USE_SIP"
/>
<uses-permissionandroid:name="android.permission.PROCESS_OUTGOING_CALLS"
/>
<uses-permissionandroid:name="com.android.voicemail.permission.ADD_VOICEMAIL"
/>
你申请了关于电话的那么多权限,在动态申请的时候,它只会弹出
1461654947945.jpg
这一个权限框
所以,这是一个权限组的概念,运行时选择你申请的同组权限的一个就行
目前所有的危险权限组集合
<!-- Dangerous Permissions. -->
<!-- 联系人 -->
<uses-permissionandroid:name="android.permission.WRITE_CONTACTS"
/>
<uses-permissionandroid:name="android.permission.GET_ACCOUNTS"
/>
<uses-permissionandroid:name="android.permission.READ_CONTACTS"
/>
<!-- 录音 -->
<uses-permissionandroid:name="android.permission.RECORD_AUDIO"
/>
<!-- 电话 -->
<uses-permissionandroid:name="android.permission.READ_CALL_LOG"
/>
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"
/>
<uses-permissionandroid:name="android.permission.CALL_PHONE"
/>
<uses-permissionandroid:name="android.permission.WRITE_CALL_LOG"
/>
<uses-permissionandroid:name="android.permission.USE_SIP"
/>
<uses-permissionandroid:name="android.permission.PROCESS_OUTGOING_CALLS"
/>
<uses-permissionandroid:name="com.android.voicemail.permission.ADD_VOICEMAIL"
/>
<!-- 日历 -->
<uses-permissionandroid:name="android.permission.READ_CALENDAR"
/>
<uses-permissionandroid:name="android.permission.WRITE_CALENDAR"
/>
<!-- 相机 -->
<uses-permissionandroid:name="android.permission.CAMERA"
/>
<!-- 传感器 -->
<uses-permissionandroid:name="android.permission.BODY_SENSORS"
/>
<!-- 定位 -->
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"
/>
<uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"
/>
<!-- 存储 -->
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"
/>
<!-- 短信 -->
<uses-permissionandroid:name="android.permission.READ_SMS"
/>
<uses-permissionandroid:name="android.permission.RECEIVE_WAP_PUSH"
/>
<uses-permissionandroid:name="android.permission.RECEIVE_MMS"
/>
<uses-permissionandroid:name="android.permission.RECEIVE_SMS"
/>
<uses-permissionandroid:name="android.permission.SEND_SMS"
/>
对应的java code
// 联系人
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.GET_ACCOUNTS,
Manifest.permission.READ_CONTACTS,
// 电话
Manifest.permission.READ_CALL_LOG,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CALL_PHONE,
Manifest.permission.WRITE_CALL_LOG,
Manifest.permission.USE_SIP,
Manifest.permission.PROCESS_OUTGOING_CALLS,
Manifest.permission.ADD_VOICEMAIL,
// 日历
Manifest.permission.READ_CALENDAR,
Manifest.permission.WRITE_CALENDAR,
// 相机
Manifest.permission.CAMERA,
// 传感器
Manifest.permission.BODY_SENSORS,
// 定位
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
// 存储
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
// 录音
Manifest.permission.RECORD_AUDIO,
// 短信
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_WAP_PUSH,
Manifest.permission.RECEIVE_MMS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.SEND_SMS,
运行时权限请求的基本步骤
1.在xml中注册2. 运行时请求权限
以下是权限检查的帮助类
/**
* 检查权限是否已请求到 (6.0)
*/
publicvoid
checkPermissions(String... permissions){
// 版本兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
// 判断缺失哪些必要权限
&& lacksPermissions(permissions)) {
// 如果缺失,则申请
requestPermissions(permissions);
}
}
/**
* 判断是否缺失权限集合中的权限
*/
privateboolean
lacksPermissions(String... permissions){
for (String permission : permissions) {
if (lacksPermission(permission)) {
return
true;
}
}
return
false;
}
/**
* 判断是否缺少某个权限
*/
privateboolean
lacksPermission(String permission){
return ContextCompat.checkSelfPermission(context, permission) ==
PackageManager.PERMISSION_DENIED;
}
/**
* 请求权限
*/
privatevoid
requestPermissions(String... permissions){
ActivityCompat.requestPermissions(context, permissions, PERMISSION_REQUEST_CODE);
}
/**
* 启动应用的设置,进入手动配置权限页面
*/
privatevoid
startAppSettings()
{
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(),null);
intent.setData(uri);
context.startActivity(intent);
}
注意: 其中的requestPermissions
方法,它会弹出权限提示框( 没有点击不再提醒的话 ),然后调用public void onRequestPermissionsResult(int
requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法,该方法在Activity或者Fragment中回调
3.在onRequestPermissionsResult回调中处理
在
public void onRequestPermissionsResult() 方法中,你可以捕获到用于是点击了
不再提醒 还是
拒绝 ,然后做出不同的操作..
@Override
publicvoid
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNullint[]
grantResults){
// 版本兼容
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M ||
requestCode != PermissionsChecker.PERMISSION_REQUEST_CODE)
return;
for (int i =0, len = permissions.length; i < len; i++) {
String permission = permissions[i];
// 缺失的权限
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
boolean showRationale = shouldShowRequestPermissionRationale(permission);
if (!showRationale) {
// 用户点击不再提醒
// TODO
break;
} else {
// 用户点击了取消...
// possibly check more permissions...
}
}
}
}
权限请求策略
下面提供一种我认为还不错的策略在需要某权限的Activity的
onStrart() 中去请求权限
在
onRequestPermissionsResult 回调中,如果用户点击了拒绝,则继续请求权限
如果用户点击了不再提醒,则弹出自定义对话框,引导用户手动去开启权限,如果用户不授权,则退出当前页面
注意:适用于没有权限就无法使用该功能的情况
1461661615410.jpg
Activity代码
public
class
BaseActivity extendsAppCompatActivity
{
private
static final String TAG ="BaseActivity";
private PermissionsChecker checker;
@Override
protectedvoid
onCreate(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
checker = new PermissionsChecker(this);
}
public PermissionsCheckergetChecker(){
return checker;
}
/**
* 在该声明周期,检查权限申请情况
*/
@Override
protectedvoid
onStart()
{
super.onStart();
checker.checkPermissions(PermissionsChecker.PERMISSIONS);
}
/**
* 请求权限检查完后回调的结果
*
* @param requestCode .
* @param permissions 所请求的权限
* @param grantResults .
*/
@TargetApi(Build.VERSION_CODES.M)
@Override
publicvoid
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNullint[]
grantResults){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
requestCode != PermissionsChecker.PERMISSION_REQUEST_CODE)
return;
for (int i =0, len = permissions.length; i < len; i++) {
String permission = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
boolean showRationale = shouldShowRequestPermissionRationale(permission);
if (!showRationale) {
// 用户点击不再提醒,弹出权限框,引导其手动开启权限
checker.showMissingPermissionDialog();
break;
} else {
// 用户点击取消,继续提示
checker.checkPermissions(PermissionsChecker.PERMISSIONS);
break;
}
}
}
}
}
Fragment中运行时权限的特殊处理
在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方法,onRequestPermissionsResult不会回调回来,建议使用getParentFragment().requestPermissions方法,
这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragment fragment : fragments) { if (fragment != null) { fragment.onRequestPermissionsResult(requestCode,permissions,grantResults); } } } } |
相关开源项目
PermissionsDispatcher使用标注的方式,动态生成类处理运行时权限,目前还不支持嵌套Fragment。
:https://github.com/hotchemi/PermissionsDispatcher
RxPermissions
基于RxJava的运行时权限检测框架
:https://github.com/tbruyelle/RxPermissions
Grant
简化运行时权限的处理,比较灵活
https://github.com/anthonycr/Grant
android-RuntimePermissions
Google官方的例子
https://github.com/googlesamples/android-RuntimePermissions
附录
以下权限只需要在AndroidManifest.xml中声明即可使用android.permission.ACCESS_LOCATION_EXTRA_COMMANDS android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_NOTIFICATION_POLICY android.permission.ACCESS_WIFI_STATE android.permission.ACCESS_WIMAX_STATE android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.BROADCAST_STICKY android.permission.CHANGE_NETWORK_STATE android.permission.CHANGE_WIFI_MULTICAST_STATE android.permission.CHANGE_WIFI_STATE android.permission.CHANGE_WIMAX_STATE android.permission.DISABLE_KEYGUARD android.permission.EXPAND_STATUS_BAR android.permission.FLASHLIGHT android.permission.GET_ACCOUNTS android.permission.GET_PACKAGE_SIZE android.permission.INTERNET android.permission.KILL_BACKGROUND_PROCESSES android.permission.MODIFY_AUDIO_SETTINGS android.permission.NFC android.permission.READ_SYNC_SETTINGS android.permission.READ_SYNC_STATS android.permission.RECEIVE_BOOT_COMPLETED android.permission.REORDER_TASKS android.permission.REQUEST_INSTALL_PACKAGES android.permission.SET_TIME_ZONE android.permission.SET_WALLPAPER android.permission.SET_WALLPAPER_HINTS android.permission.SUBSCRIBED_FEEDS_READ android.permission.TRANSMIT_IR android.permission.USE_FINGERPRINT android.permission.VIBRATE android.permission.WAKE_LOCK android.permission.WRITE_SYNC_SETTINGS com.android.alarm.permission.SET_ALARM com.android.launcher.permission.INSTALL_SHORTCUT com.android.launcher.permission.UNINSTALL_SHORTCUT |
相关文章推荐
- android6.0的坑
- Android M新的运行时权限开发者需要知道的一切
- Android6.0权限申请讲解及Demo实例
- Android 6.0 运行时权限处理终结全网请求权限开源库出现的各种坑包括认为是google的bug
- Android6.0运行时权限笔记。
- Android 6.0 运行时权限处理完全解析
- 一个反编译看代码的途径和android6.0权限控制实例
- (鸿洋)Android 6.0 运行时权限处理完全解析
- android6.0运行时权限完美封装
- android系统6.0权限
- Android进程启动过程源码分析整理.
- android:DDMS查看Threads
- Android5.0中Binder机制相关的native层的Parcel分析
- Android5.0中Binder机制相关的native层和java层中的Binder代理和实体的对象的分析
- android Seekbar 拖动按钮显示不全问题
- Android5.0中Binder相关的ProcessState和IPCThreadState的认识.
- Android代码中动态改变字体颜色
- Android之ViewPager总结(三)
- Android5.0中Binder机制相关的常见的结构体和函数.
- android 应用中短信发送