网络科技

    今日:25| 主题:301306
收藏本版
互联网、科技极客的综合动态。

[其他] Android 6.0运行时请求权限

[复制链接]
电脑学堂 发表于 2016-11-28 07:48:43
191 5
Android权限简介

   从Android 6.0开始,部分 危险权限 需要在运行时用户动态授权,因为一个Android应用默认情况下是不拥有任何权限的。在开发的时候,我们会在AndroidManifest.xml中静态地声明相应的权限,如果没有声明该权限却使用了相应的权限,程序会崩溃,抛出异常,例如,如果没有在程序中声明网络权限当我们使用网络的时候,就会抛出如下异常,而且一般不会try catch该异常:
  Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)

   

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  但是在Android6.0上面部分权限不但要求开发者在使用之前在AndroidManifest.xml声明,还需要用户在使用的时候进行授权才可以使用,例如用户想使用拍照上传图片,虽然已经声明了使用相机的权限,如果用户拒绝使用相机权限,还是会抛出异常:
  Caused by: java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cmp=com.android.camera/.Camera }…

  这时候如果程序员没有对Android6.0手机进行额外权限的处理,程序就会直接崩掉了,给用户带来很不好的体验。
  刚刚看到一篇新闻,《Android 7.1.1将于12月5日正式登陆Nexus设备》,Android7.x的手机都要问世了,针对Android M以上的手机动态权限确实需要开发者认真研究一下了。
  正常权限和危险权限

   事实上Android系统对权限声明有四个等级,主要通过protectionLevel来设置,具体可以参考 https://developer.android.com/guide/topics/manifest/permission-element.html
  1. <permissionandroid:description="string resource"
  2. android:icon="drawable resource"
  3. android:label="string resource"
  4. android:name="string"
  5. android:permissionGroup="string"
  6. android:protectionLevel=["normal" | "dangerous" |
  7. "signature" | "signatureOrSystem"]/>
复制代码
  签名相关的两个权限并不常用,在开发中我们真正需要针对Android6.0以上系统进行特殊处理的都是危险权限,更为详细的介绍可以参看 https://developer.android.com/guide/topics/security/permissions.html
   系统权限分为两类: 正常权限危险权限
  
       
  • 正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。   
  • 危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。  
   之所以在这里列出来 正常权限危险权限 ,主要就是因为Android6.0在这里做了一个分割线:
  
       
  • 如果设备运行的是 Android 5.1 或更低版本, 或者 应用的目标 SDK 为 22 或更低:如果您在清单中列出了危险权限,则用户必须在安装应用时授予此权限;如果他们不授予此权限,系统根本不会安装应用。   
  • 如果设备运行的是 Android 6.0 或更高版本, 或者 应用的目标 SDK 为 23 或更高:应用必须在清单中列出权限, 并且 它必须在运行时请求其需要的每项危险权限。用户可以授予或拒绝每项权限,且即使用户拒绝权限请求,应用仍可以继续运行有限的功能。  
  权限组

  所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:
  
       
  • 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。   
  • 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS ,系统将立即授予该权限。  
  任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。
              权限组     权限              CALENDAR            
          
  • READ_CALENDAR      
      
          
  • WRITE_CALENDAR      
               CAMERA            
          
  • CAMERA      
               CONTACTS            
          
  • READ_CONTACTS      
  • WRITE_CONTACTS      
  • GET_ACCOUNTS      
               LOCATION            
          
  • ACCESS_FINE_LOCATION      
  • ACCESS_COARSE_LOCATION      
               MICROPHONE            
          
  • RECORD_AUDIO      
               PHONE            
          
  • READ_PHONE_STATE      
  • CALL_PHONE      
  • READ_CALL_LOG      
  • WRITE_CALL_LOG      
  • ADD_VOICEMAIL      
  • USE_SIP      
  • PROCESS_OUTGOING_CALLS      
               SENSORS            
          
  • BODY_SENSORS      
               SMS            
          
  • SEND_SMS      
  • RECEIVE_SMS      
  • READ_SMS      
  • RECEIVE_WAP_PUSH      
  • RECEIVE_MMS      
               STORAGE            
          
  • READ_EXTERNAL_STORAGE      
  • WRITE_EXTERNAL_STORAGE      
            动态权限处理逻辑

  Android6.0动态权限处理一般有三个步骤:
  
       
  • 检查权限;   
  • 请求权限;   
  • 处理请求相应回调。  
  虽然Android6.0已经新增了针对动态权限的相关实现方法,但是为了使用方便我们使用support支持看,这样免去了判断Android版本的繁琐,针对Activity和Fragment支持库都提供了相应的方法。
  1. int ContextCompat.checkSelfPermission(Contextcontext, String permission)
  2. void ActivityCompat.requestPermissions(Activityactivity, String[] permissions, int requestCode)
  3. boolean ActivityCompat.shouldShowRequestPermissionRationale(Activityactivity, String permission)
  4. void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
  5. //fragment
  6. void requestPermissions(String[] permissions, int requestCode)
复制代码
检查权限

  当我们开发的应用在操作是需要一个危险的权限,在执行操作时应该检查一下是否具有该权限。检查是否具有某项权限,可以调用 ContextCompat.checkSelfPermission() 方法。如果应用具有该权限,方法将返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回 PERMISSION_DENIED,且应用必须明确向用户要求权限。
  下面是一个调用相机的权限:
  1. int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
  2. if (permission == PackageManager.PERMISSION_GRANTED) {
  3. //执行拍照
  4. } else {//请求权限
  5. ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA }, PHOTO);
  6. }
复制代码
请求权限

  在第一步中我们检查权限,如果没有相应的权限即去请求权限,请求权限时,我们可以同时请求多个权限,也可以单个权限执行请求。调用请求权限时系统会弹出来一个标准的Android对话框,开发者不能进行自定义。
   

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  当我们同时请求多个权限时,会在系统单个对话框中顺序显示多个权限授权操作,下面代码是一次请求多个权限:
  1. String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE };
  2. ActivityCompat.requestPermissions(this, permissions, PHOTO);
复制代码
  

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  处理请求相应回调

  当我们响应系统请求权限对话框时,系统将调用应用的 onRequestPermissionsResult() 方法,在回调中继续处理我们的逻辑操作,如果用户拒绝了我们要求的权限,我们可以在这里进行必要的解释,告诉用户为什么需要此权限。当然了不是每条权限都需要解释,太多的解释反而会降低用户体验。
  为了帮助开发者提供需要解释的情形,Android提供了 shouldShowRequestPermissionRationale()方法,部分手机上面该方法不起作用,主要是由于Android手机深度定制导致的。
  
       
  • 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。   
  • 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。   
  • 如果设备规范禁止应用具有该权限,此方法也会返回 false。  
   

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  下面是一个对执行拍照请求权限后的回调:
  1. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  2. switch (requestCode) {
  3. case PHOTO:
  4. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  5. //拍照
  6. } else {
  7. if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
  8. Manifest.permission.CAMERA)) {
  9. //提供必要的解释,为什么需要该权限
  10. }else{
  11. Toast.makeText(MainActivity.this, "授权失败!", 0).show();
  12. }
  13. }
  14. break;
  15. default:
  16. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  17. break;
  18. }
  19. }
复制代码
权限拒绝后跳转至应用的设置界面

  在动态请求权限处理时,有一种简单粗暴的方式,我们一下将应用中涉及到的危险权限一次性全部请求,如果用户不同意,直接提示用户需要相应的权限才可以使用该应用,然后跳转到应用的设置界面,当然了由于Android手机种类多样,下下面这种方法不一定总是可行,不过在小米、魅族、华为和三星手机上验证了部分手机都是可行的。
  1. IntentlocalIntent = new Intent();
  2. localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  3. localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
  4. localIntent.setData(Uri.fromParts("package", getPackageName(), null));
  5. startActivity(localIntent);
复制代码
在Android6.0之前手机设置界面基本是这样的,所有申请的权限如下排列:
   

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  在Android6.0之后新增了权限相关的设置信息:
   

Android 6.0运行时请求权限

Android 6.0运行时请求权限

  Fragment处理权限请求回调

  在使用Fragment处理回调是不要使用ActivityCompat.requestPermissions方法,虽然使用该方法不会报错,直接使用Fragment中的requestPermissions方法即可,否则就会将回调返回到相应的Activity。
  如果Fragment嵌套了子Fragment,在子Fragment中使用requestPermissions方 法,onRequestPermissionsResult不会回调回来,建议使用 getParentFragment().requestPermissions方法,这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment。
  1. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {  
  2. super.onRequestPermissionsResult(requestCode, permissions, grantResults);  
  3. List<Fragment> fragments = getChildFragmentManager().getFragments();  
  4. if (fragments != null) {  
  5. for (Fragmentfragment : fragments) {  
  6. if (fragment != null) {  
  7. fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);  
  8. }  
  9. }  
  10. }  
  11. }
复制代码
权限的最佳做法

   这里所说的最佳做法来自 https://developer.android.com/training/permissions/best-practices.html
  
       
  • 考虑使用 intent   
  • 仅要求您需要的权限   
  • 不要让用户感到无所适从      
  • 测试两种权限模式     
  另外可以参考google在github上的动态请求权限的示例或者使用第三方的库来处理权限:
   android-RuntimePermissions: https://github.com/googlesamples/android-RuntimePermissions
   easypermissions: https://github.com/googlesamples/easypermissions
   PermissionsDispatcher: https://github.com/hotchemi/PermissionsDispatcher
   RxPermissions: https://github.com/tbruyelle/RxPermissions
   Grant: https://github.com/anthonycr/Grant
  参考资料

   Android M Permission 运行时权限 学习笔记
   聊一聊Android 6.0的运行时权限
   权限最佳做法
   系统权限
   在运行时请求权限
   安卓6.0新特性在Fragment申请运行时权限
   android 6.0权限全面详细分析和解决方案
   Android 6.0 运行时权限处理
邓显波 发表于 2016-11-28 20:20:06
有谁会在时过境迁之后还在那里等你。
回复 支持 反对

使用道具 举报

123太阳 发表于 2016-11-28 20:40:45
不要迷恋楼主,楼主只是个传说。
回复 支持 反对

使用道具 举报

飞洒范德萨 发表于 2016-12-14 17:42:25
我只看看不说话。。。
回复 支持 反对

使用道具 举报

yuxiao520 发表于 2016-12-14 17:46:50
咨询电脑学堂一个非技术性问题
回复 支持 反对

使用道具 举报

玩命猜 发表于 2016-12-20 10:45:41
我也来顶一下..
回复 支持 反对

使用道具 举报

我要投稿

推荐阅读


回页顶回复上一篇下一篇回列表
手机版/c.CoLaBug.com ( 粤ICP备05003221号 | 粤公网安备 44010402000842号 )

© 2001-2017 Comsenz Inc.

返回顶部 返回列表