zoukankan      html  css  js  c++  java
  • Android 6.0 Permission权限与安全机制

    Marshmallow版本权限修改

      android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西,而且一般用户安装的时候很少会去仔细看权限列表,更不会去深入了解这些权限可能带来的相关危害。但是在android 6.0 Marshmallow版本之后,系统不会在软件安装的时候就赋予该app所有其申请的权限,对于一些危险级别的权限,app需要在运行时一个一个询问用户授予权限。

       

    旧版本app兼容问题

      那么问题来了,是不是所有以前发布的app都会出现问题呢?答案是不会,只有那些targetSdkVersion 设置为23及以上的应用才会出现异常,在使用危险权限的时候系统必须要获得用户的同意才能使用,要不然应用就会崩溃,出现类似下面的错误。

    java.lang.SecurityException: Permission Denial...

    所以targetSdkVersion如果没有设置为23版本或者以上,系统还是会使用旧规则:在安装的时候赋予该app所申请的所有权限。所以app当然可以和以前一样正常使用了,但是还有一点需要注意的是6.0的系统里面,用户可以手动将该app的权限关闭,在 App info里面Permissions下边,可以关闭某个权限。如果以前的老应用申请的权限被用户手动关闭了,不会抛出异常,不会崩溃,只不过调用那些被用户禁止权限的api接口返回值都为null或者0,所以我们只需要做一下判空操作就可以了,这是需要注意的。

    普通权限和危险权限列表

      现在对于新版本的权限变更应该有了基本的认识,那么,是不是所有权限都需要去进行特殊处理呢?当然不是,只有那些危险级别的权限才需要,可参考官网。

      http://developer.android.com/training/permissions/requesting.html 
      http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous 
      所以仔细去看看自己的app,对照列表,如果有需要申请其中的一个权限,就需要进行特殊操作。还有一个比较人性的地方就是如果同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_EXTERNAL_STORAGE被授权了,app也有READ_EXTERNAL_STORAGE权限了。

    支持Marshmallow新版本权限机制

      在Android M的api中,我们可以通过checkSelfPermission检测软件是否有某一项权限,以及使用requestPermissions去请求一组权限。

    复制代码
    int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    CODE_FOR_WRITE_PERMISSION);
        return;
    }
    复制代码

      以上的代码块展示了检测软件是否有写文件的权限,如果没有写文件的权限,则通过requestPermissions去向用户发起请求权限的流程。向用户发起请求之后,请求完成,会有相对应的回调方法,通知软件用户是否授予了权限。通过在Activity或者Fragment中重写onRequestPermissionsResult方法。  

    复制代码
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == CODE_FOR_WRITE_PERMISSION){
            if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                // 用户同意写文件
            } else {
                // 用户不同意,自行处理即可
            }
        }
    }
    复制代码

    处理不再提醒

      如果用户一旦拒绝过某权限的授权。下一次弹框时,用户会有一个“不再提醒(Never ask again)”的选项的来防止app以后继续请求授权。

      

      如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,系统会直接回调onRequestPermissionsResult函数,回调结果为最后一次用户的选择。所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况。

    1. 应用安装后第一次访问,直接返回false;
    2. 第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回 true,这时候可以显示一些为什么需要这个权限的说明;
    3. 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时:shouldShowRequestPermissionRationale()返回 false;
    4. 设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false;

    注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale()也会一直返回true。

    所以利用这个函数我们可以进行相应的优化,针对shouldShowRequestPermissionRationale函数返回false的处理有两种方法:

      处理方法已经有了,修改一下代码,这里以第二种方案来处理:

    复制代码
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == CODE_FOR_WRITE_PERMISSION){
            if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                // 用户同意
            } else {
                // 用户不同意,向用户展示该权限作用
                if (!shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    showPermissionDialog();return;
                }
            }
        }
    }
    复制代码

    当勾选不再提醒,并且拒绝之后,弹出dialog,提醒用户该权限的重要性

    使用兼容库

      以上的代码在6.0版本上使用没有问题,但是在之前就有问题了,最简单粗暴的解决方法可能就是利用Build.VERSION.SDK_INT >= 23这个判断语句来判断了,方便的是SDK 23的v4包加入了专门类进行相关的处理:

    • ContextCompat.checkSelfPermission()被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED ,在所有版本都是如此。
    • ActivityCompat.requestPermissions()这个方法在6.0之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者PERMISSION_DENIED。
    • ActivityCompat.shouldShowRequestPermissionRationale()在6.0之前版本调用,永远返回false。
    复制代码
    // 使用兼容库就无需判断系统版本
    int hasWriteContactsPermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (hasWriteContactsPermission == PackageManager.PERMISSION_GRANTED) {
     
    } // 需要弹出dialog让用户手动赋予权限
    else {
        ActivityCompat.requestPermissions(Acivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_FOR_WRITE_PERMISSION);
    }
    复制代码

    onRequestPermissionsResult函数不变。后两个方法,我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions() 和 FragmentCompat.shouldShowRequestPermissionRationale()和activity效果一样。

    一次请求多个权限

      当然了有时候需要多个权限,可以用上面方法一次请求多个权限。当然最重要的是不要忘了为每个权限检查“不再提醒”的设置。

    List<String> permissionsNeeded = new ArrayList<String>();
    permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
    permissionsNeeded.add(Manifest.permission.READ_CONTACTS);
    permissionsNeeded.add(Manifest.permission.WRITE_CONTACTS);
    requestPermissions(permissionsNeeded.toArray(new String[permissionsList.size()]), CODE_FOR_MULTIPLE_PERMISSION);

    最后在onRequestPermissionsResult函数中一个个处理返回结果即可。

    第三方库简化代码

      当然早就有第三方库来帮忙做这些事情了: 
      Github上的开源项目 PermissionHelperPermissionsDispatcherEasyPermissions

    APP处于运行状态下,被撤销权限

      如果APP正在运行中,用户进入设置-应用程序页面去手动撤销该APP权限,会出现什么情况呢?系统又会接着弹出权限请求对话框。

    Over

      新运行时权限已经在棉花糖中被使用了。我们没有退路。我们现在唯一能做的就是保证app适配新权限模型。欣慰的是只有少数权限需要运行时权限模型。大多数常用的权限,例如,网络访问,属于Normal Permission 在安装时自动会授权,当然你要声明,以后无需检查。因此,只有少部分代码你需要修改。 
    两个建议: 
      1.严肃对待新权限模型。 
      2.如果你代码没支持新权限,不要设置targetSdkVersion 23 。尤其是当你在Studio新建工程时,不要忘了修改! 
      说一下代码修改。这是大事,如果代码结构被设计的不够好,你需要一些很蛋疼的重构。每个app都要被修正。如上所说,我们没的选择。列出所有你需要请求权限的全部情形,如果A被授权,B被拒绝,会发生什么,针对每一个情况认真处理。

  • 相关阅读:
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    FFmpeg解码H264及swscale缩放详解
    linux中cat more less head tail 命令区别
    C语言字符串操作总结大全(超详细)
    如何使用eclipse进行嵌入式Linux的开发
  • 原文地址:https://www.cnblogs.com/pangguoming/p/7343859.html
Copyright © 2011-2022 走看看