Android6.0权限管理

运行时权限(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 吧,但这绝对不是长久之计...(丑陋...)

  1. android { 
  2. compileSdkVersion 23 
  3. buildToolsVersion "23.0.2" 
  4.  
  5. defaultConfig { 
  6. minSdkVersion 8 
  7. targetSdkVersion22 
  8. versionCode 1 
  9. versionName "1.0" 

  10. buildTypes { 
  11. release { 
  12. minifyEnabled false 
  13. proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro' 



enter description here

1461662965441.jpg

6.0权限弹框的两种模式

1.初次请求,弹出对话框叫你勾选

enter description here

1461654947945.jpg

2,第二次请求之后,弹出对话框,出现不再提醒字样

enter description here

1461655014689.jpg

当用户点击了不再提醒,你再次请求权限的时候,就不会弹出对话框,所以这时,你需要根据需求另做处理
下面会如何处理

6.0之后的权限分类

分为两类 Normal permissions Dangerous permissions

Normal permissions(普通权限)

只需要在xml中申请就可以了,与6.0之前没什么区别
包括的权限有

  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 

其实不需要记,记住哪些是危险权限就是了

Dangerous permissions(危险权限)

危险权限,需要在运行时请求.
注意: 危险权限是按组来分的,所以,当你申请了多个同组的危险权限时,运行时只需要申请一个就行
例如:

  1. <!-- 电话 --> 
  2. <uses-permissionandroid:name="android.permission.READ_CALL_LOG" /> 
  3. <uses-permissionandroid:name="android.permission.READ_PHONE_STATE" /> 
  4. <uses-permissionandroid:name="android.permission.CALL_PHONE" /> 
  5. <uses-permissionandroid:name="android.permission.WRITE_CALL_LOG" /> 
  6. <uses-permissionandroid:name="android.permission.USE_SIP" /> 
  7. <uses-permissionandroid:name="android.permission.PROCESS_OUTGOING_CALLS" /> 
  8. <uses-permissionandroid:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> 

你申请了关于电话的那么多权限,在动态申请的时候,它只会弹出

enter description here

1461654947945.jpg

这一个权限框
所以,这是一个权限组的概念,运行时选择你申请的同组权限的一个就行

目前所有的危险权限组集合

  1. <!-- Dangerous Permissions. --> 
  2. <!-- 联系人 --> 
  3. <uses-permissionandroid:name="android.permission.WRITE_CONTACTS" /> 
  4. <uses-permissionandroid:name="android.permission.GET_ACCOUNTS" /> 
  5. <uses-permissionandroid:name="android.permission.READ_CONTACTS" /> 
  6.  
  7. <!-- 录音 --> 
  8. <uses-permissionandroid:name="android.permission.RECORD_AUDIO" /> 
  9.  
  10. <!-- 电话 --> 
  11. <uses-permissionandroid:name="android.permission.READ_CALL_LOG" /> 
  12. <uses-permissionandroid:name="android.permission.READ_PHONE_STATE" /> 
  13. <uses-permissionandroid:name="android.permission.CALL_PHONE" /> 
  14. <uses-permissionandroid:name="android.permission.WRITE_CALL_LOG" /> 
  15. <uses-permissionandroid:name="android.permission.USE_SIP" /> 
  16. <uses-permissionandroid:name="android.permission.PROCESS_OUTGOING_CALLS" /> 
  17. <uses-permissionandroid:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> 
  18.  
  19. <!-- 日历 --> 
  20. <uses-permissionandroid:name="android.permission.READ_CALENDAR" /> 
  21. <uses-permissionandroid:name="android.permission.WRITE_CALENDAR" /> 
  22.  
  23. <!-- 相机 --> 
  24. <uses-permissionandroid:name="android.permission.CAMERA" /> 
  25.  
  26. <!-- 传感器 --> 
  27. <uses-permissionandroid:name="android.permission.BODY_SENSORS" /> 
  28.  
  29. <!-- 定位 --> 
  30. <uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" /> 
  31. <uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION" /> 
  32.  
  33. <!-- 存储 --> 
  34. <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
  35. <uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE" /> 
  36.  
  37. <!-- 短信 --> 
  38. <uses-permissionandroid:name="android.permission.READ_SMS" /> 
  39. <uses-permissionandroid:name="android.permission.RECEIVE_WAP_PUSH" /> 
  40. <uses-permissionandroid:name="android.permission.RECEIVE_MMS" /> 
  41. <uses-permissionandroid:name="android.permission.RECEIVE_SMS" /> 
  42. <uses-permissionandroid:name="android.permission.SEND_SMS" /> 

对应的java code

  1. // 联系人 
  2. Manifest.permission.WRITE_CONTACTS, 
  3. Manifest.permission.GET_ACCOUNTS, 
  4. Manifest.permission.READ_CONTACTS, 
  5.  
  6. // 电话 
  7. Manifest.permission.READ_CALL_LOG, 
  8. Manifest.permission.READ_PHONE_STATE, 
  9. Manifest.permission.CALL_PHONE, 
  10. Manifest.permission.WRITE_CALL_LOG, 
  11. Manifest.permission.USE_SIP, 
  12. Manifest.permission.PROCESS_OUTGOING_CALLS, 
  13. Manifest.permission.ADD_VOICEMAIL, 
  14.  
  15. // 日历 
  16. Manifest.permission.READ_CALENDAR, 
  17. Manifest.permission.WRITE_CALENDAR, 
  18.  
  19. // 相机 
  20. Manifest.permission.CAMERA, 
  21.  
  22. // 传感器 
  23. Manifest.permission.BODY_SENSORS, 
  24.  
  25. // 定位 
  26. Manifest.permission.ACCESS_FINE_LOCATION, 
  27. Manifest.permission.ACCESS_COARSE_LOCATION, 
  28.  
  29. // 存储 
  30. Manifest.permission.READ_EXTERNAL_STORAGE, 
  31. Manifest.permission.WRITE_EXTERNAL_STORAGE, 
  32.  
  33. // 录音 
  34. Manifest.permission.RECORD_AUDIO, 
  35.  
  36. // 短信 
  37. Manifest.permission.READ_SMS, 
  38. Manifest.permission.RECEIVE_WAP_PUSH, 
  39. Manifest.permission.RECEIVE_MMS, 
  40. Manifest.permission.RECEIVE_SMS, 
  41. Manifest.permission.SEND_SMS, 

运行时权限请求的基本步骤

1.在xml中注册
2. 运行时请求权限

以下是权限检查的帮助类

  1. /** 
  2. * 检查权限是否已请求到 (6.0) 
  3. */ 
  4. publicvoid checkPermissions(String... permissions)
  5. // 版本兼容 
  6. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 
  7. // 判断缺失哪些必要权限 
  8. && lacksPermissions(permissions)) { 
  9. // 如果缺失,则申请 
  10. requestPermissions(permissions); 


  11.  
  12. /** 
  13. * 判断是否缺失权限集合中的权限 
  14. */ 
  15. privateboolean lacksPermissions(String... permissions)
  16. for (String permission : permissions) { 
  17. if (lacksPermission(permission)) { 
  18. return true


  19. return false

  20.  
  21. /** 
  22. * 判断是否缺少某个权限 
  23. */ 
  24. privateboolean lacksPermission(String permission)
  25. return ContextCompat.checkSelfPermission(context, permission) == 
  26. PackageManager.PERMISSION_DENIED; 

  27.  
  28. /** 
  29. * 请求权限 
  30. */ 
  31. privatevoid requestPermissions(String... permissions)
  32. ActivityCompat.requestPermissions(context, permissions, PERMISSION_REQUEST_CODE); 

  33.  
  34. /** 
  35. * 启动应用的设置,进入手动配置权限页面 
  36. */ 
  37. privatevoid startAppSettings()
  38. Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 
  39. Uri uri = Uri.fromParts("package", context.getPackageName(),null); 
  40. intent.setData(uri); 
  41. context.startActivity(intent); 

注意: 其中的requestPermissions 方法,它会弹出权限提示框( 没有点击不再提醒的话 ),然后调用public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法,该方法在Activity或者Fragment中回调

3.在onRequestPermissionsResult回调中处理

public void onRequestPermissionsResult() 方法中,你可以捕获到用于是点击了 不再提醒 还是 拒绝 ,然后做出不同的操作..

  1. @Override 
  2. publicvoid onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNullint[] grantResults)
  3. // 版本兼容 
  4. if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M || 
  5. requestCode != PermissionsChecker.PERMISSION_REQUEST_CODE) 
  6. return
  7.  
  8. for (int i =0, len = permissions.length; i < len; i++) { 
  9. String permission = permissions[i]; 
  10. // 缺失的权限 
  11. if (grantResults[i] == PackageManager.PERMISSION_DENIED) { 
  12. boolean showRationale = shouldShowRequestPermissionRationale(permission); 
  13. if (!showRationale) { 
  14. // 用户点击不再提醒 
  15. // TODO 
  16. break
  17. } else {  
  18. // 用户点击了取消... 
  19. // possibly check more permissions... 




权限请求策略

下面提供一种我认为还不错的策略
在需要某权限的Activity的 onStrart() 中去请求权限
onRequestPermissionsResult 回调中,如果用户点击了拒绝,则继续请求权限
如果用户点击了不再提醒,则弹出自定义对话框,引导用户手动去开启权限,如果用户不授权,则退出当前页面
注意:适用于没有权限就无法使用该功能的情况

enter description here

1461661615410.jpg

Activity代码

  1. public class BaseActivity extendsAppCompatActivity
  2. private static final String TAG ="BaseActivity"
  3. private PermissionsChecker checker; 
  4.  
  5. @Override 
  6. protectedvoid onCreate(@Nullable Bundle savedInstanceState)
  7. super.onCreate(savedInstanceState); 
  8. checker = new PermissionsChecker(this); 

  9.  
  10. public PermissionsCheckergetChecker()
  11. return checker; 

  12.  
  13. /** 
  14. * 在该声明周期,检查权限申请情况 
  15. */ 
  16. @Override 
  17. protectedvoid onStart()
  18. super.onStart(); 
  19. checker.checkPermissions(PermissionsChecker.PERMISSIONS); 

  20.  
  21. /** 
  22. * 请求权限检查完后回调的结果 
  23. * 
  24. * @param requestCode . 
  25. * @param permissions 所请求的权限 
  26. * @param grantResults . 
  27. */ 
  28. @TargetApi(Build.VERSION_CODES.M) 
  29. @Override 
  30. publicvoid onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNullint[] grantResults)
  31. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || 
  32. requestCode != PermissionsChecker.PERMISSION_REQUEST_CODE) 
  33. return
  34.  
  35.  
  36. for (int i =0, len = permissions.length; i < len; i++) { 
  37. String permission = permissions[i]; 
  38. if (grantResults[i] == PackageManager.PERMISSION_DENIED) { 
  39. boolean showRationale = shouldShowRequestPermissionRationale(permission); 
  40. if (!showRationale) { 
  41. // 用户点击不再提醒,弹出权限框,引导其手动开启权限 
  42. checker.showMissingPermissionDialog(); 
  43. break
  44. } else
  45. // 用户点击取消,继续提示 
  46. checker.checkPermissions(PermissionsChecker.PERMISSIONS); 
  47. break





  48.  

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




转载自:http://www.cnblogs.com/onespieces/p/5435852.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值