博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 6.0运行时权限
阅读量:6232 次
发布时间:2019-06-21

本文共 6887 字,大约阅读时间需要 22 分钟。

hot3.png

一、Android 6.0运行时权限

        在Android6.0之前,普遍意义上如果在Manifest中注册了权限,在安装过程中默认开启了权限,此后也无法关闭,这种方式相当不安全,尤其可能访问敏感信息。在Android 6.0到来了,为了解决此类不安全的问题,权限可以在系统设置中开启关闭,在Manifest中的声明只是作为权限申请“意向”,只是给了个清单,告诉系统我可能需要这些权限。在运行时,这些权限默认中一些安全级别较高的是关闭的,因此需要在运行时询问开启或者关闭。

        新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是正常权限,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是风险权限,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

 

二、面临的问题—系统碎片化

        Android 是开放系统,由于系统的版本的更新控制权不是google独有,其他ROM厂家自己设计的ROM或多或少在兼容上出现了问题。小米 android 6.0,vivo、oppo权限检测并不是通过Google提供的方式,而是通过AppOpsManager来实现的,此外检测结果还有小米自家的【询问模式,mode值为4】。最坑的是vivo android 7.0在访问通讯录时可以绕过AppOpsManager和checkPermission,直接在cursor查询时提示,权限拒绝时cursor也没有触发异常,如果个人通讯录没有联系人的话,你永远无法知道授权成功了没有。其他手机还有定时自动禁止权限问题,这都是需要注意的地方。

 

三、权限检测方法

权限检测方法,google提供了比较完善的检测机制,当然开源的EasyPremission比较简陋,AndPermission相对要好一些。

public final class PermissionChecker {    /** 已授权 */    public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;    /** 拒绝授权 */    public static final int PERMISSION_DENIED =  PackageManager.PERMISSION_DENIED;    /** 权限允许,权限操作被拒 */    public static final int PERMISSION_DENIED_APP_OP =  PackageManager.PERMISSION_DENIED  - 1;    @IntDef({PERMISSION_GRANTED,            PERMISSION_DENIED,            PERMISSION_DENIED_APP_OP})    @Retention(RetentionPolicy.SOURCE)    public @interface PermissionResult {}    private PermissionChecker() {        /* do nothing */    }    /**     * 检查指定Uid和pid进程的app权限     *      *     * @param context Context 上下文     * @param permission  权限名称 如Manifest.permission.READ_CONTACTS【注意:可以是权限组】     * @param pid 被检测app的进程id     * @param uid 被检测app的uid     * @param packageName 被检测app的包名     * @return 返回结果 {@link #PERMISSION_GRANTED}     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.     */    public static int checkPermission(@NonNull Context context, @NonNull String permission,            int pid, int uid, String packageName) {        if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {            return PERMISSION_DENIED;        }        String op = AppOpsManagerCompat.permissionToOp(permission);        if (op == null) {            return PERMISSION_GRANTED;        }        if (packageName == null) {            String[] packageNames = context.getPackageManager().getPackagesForUid(uid);            if (packageNames == null || packageNames.length <= 0) {                return PERMISSION_DENIED;            }            packageName = packageNames[0];        }        if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)                != AppOpsManagerCompat.MODE_ALLOWED) {            return PERMISSION_DENIED_APP_OP;        }        return PERMISSION_GRANTED;    }    /**     * 检测当前app的权限     *     * @param context 上下文资源     * @param permission 权限名称,也可以是权限组     * @return 返回结果 {@link #PERMISSION_GRANTED}     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.     */    public static int checkSelfPermission(@NonNull Context context,            @NonNull String permission) {        return checkPermission(context, permission, android.os.Process.myPid(),                android.os.Process.myUid(), context.getPackageName());    }    /**     * 检测其他app是否具有通过ipc调用本应用的权限     *     * @param context 上下文     * @param permission 要检查的权限     * @param packageName 调用者的包名,如果是null,则默认根据uid获取包名中的第一个包名     * @return The permission check result which is either {@link #PERMISSION_GRANTED}     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.     */    public static int checkCallingPermission(@NonNull Context context,            @NonNull String permission, String packageName) {        if (Binder.getCallingPid() == Process.myPid()) {  //            return PackageManager.PERMISSION_DENIED;        }        return checkPermission(context, permission, Binder.getCallingPid(),                Binder.getCallingUid(), packageName);    }    /**     * 检查是否所有应用和当前应用具有调用自身或者其他应用的权限     *     * @param context 上下文     * @param permission 要检查的权限     * @return 返回值 {@link #PERMISSION_GRANTED}     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.     */    public static int checkCallingOrSelfPermission(@NonNull Context context,            @NonNull String permission) {        String packageName = (Binder.getCallingPid() == Process.myPid())                ? context.getPackageName() : null;        return checkPermission(context, permission, Binder.getCallingPid(),                Binder.getCallingUid(), packageName);    }}

四、权限申请

权限申请是最复杂的过程,这个过程中涉及【系统授权对话框】问题。这里我们首先要探讨shouldShowRequestPermissionRationale方法。

这个方法的用法不能按照google官网例子来处理,否则你可能按照进入逻辑陷阱。我们先来分析一下返回值。

ActivityCompat.shouldShowRequestPermissionRationale(Context context,            String permisssion)

返回值有已下几种情况

  • 1、权限未开启的情况下,没有进行申请过permission,那么返回值为false
  • 2、权限未开启的情况下,如果申请过权限,权限被拒绝,但是没有选择【记住不再提示】,那么会返回true
  • 3、权限未开启的情况下,如果申请过权限,权限被拒绝,如果选择了【记住不再提示】,那么会返回位false
  • 4、权限已开启,并且授权成功,返回位false。

那么如何正确使用此方法了?

正确的方式是在onRequestPermissionsResult中使用,我们可以将返回的结果存入SharedPreferences的boolean值。然后在我们申请权限的额时候取出此boolean值进行判断。

注意:就算缓存被清空,那么再一次请求权限,依然会有结果回调到onRequestPermisssionsResult中

申请授权:

if (PermissionChecker.checkSelfPermission(thisActivity,                Manifest.permission.READ_CONTACTS)        != PackageManager.PERMISSION_GRANTED) {     String op = AppOpsManagerCompat.permissionToOp(Manifest.permission.READ_CONTACTS);    boolean hasNext = ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,            perm);     boolean isShowRequestTip =  !TextUtils.isEmpty(op) && SharePrefUtils.getBoolean(op,true) || hasNext ;    //注意,SharePrefUtils默认值为true,首次应该返回true    if (isShowRequestTip ) {                ActivityCompat.requestPermissions(thisActivity,                new String[]{Manifest.permission.READ_CONTACTS},                MY_PERMISSIONS_REQUEST_READ_CONTACTS);    } else {      showDialogForSystem();// 通过自己定义的dialog引导用户去设置页面授权           }}else{    Toast.make(thisActivity,"已授权,可以调用通讯录",Toast.LENGTH_SHORT).show();}

 

注意:考虑到用户授权之后有可能在设置页面关闭授权,因此,我们上面的请求权限应该做本地和系统两套判断较为合适,代码如下:

boolean hasNext = ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,            perm);     boolean isShowRequestTip =  !TextUtils.isEmpty(op) && SharePrefUtils.getBoolean(op,true) || hasNext ;

 

处理授权结果:

@Overridepublic void onRequestPermissionsResult(int requestCode,        String permissions[], int[] grantResults) {             if(MY_PERMISSIONS_REQUEST_READ_CONTACTS!=requestCode) return;       if(permissions==null || permissions.length==0) return;              //在小米,vivo中,grantResults并不可靠,因此我们还需要使用PermissionChecker进行检测       List
grantList = new ArrayList<>(); List
diniedList = new ArrayList<>(); for(int i=0;i

 

五、补充

以上只是涉及到Android 6.0的系统,还有需要很多需要完善的地方:

  • Android6.0的之前的权限检测应该默认授权
  • Fragment或者Context的检测并没有实现
  • 小米和vivo通讯录兼容,小米无法询问,我们要让他去设置页面,vivo手机可以绕过检测,这也是需要处理的问题。

 

 

转载于:https://my.oschina.net/ososchina/blog/1787953

你可能感兴趣的文章
微信支付申请90%的商户都卡在这儿了,申请微信支付,商户功能设置详细说明...
查看>>
高仿Instagram 页面效果android特效
查看>>
2016 年总结
查看>>
将String转化成Stream,将Stream转换成String
查看>>
【工具使用系列】关于 MATLAB 遗传算法与直接搜索工具箱,你需要知道的事
查看>>
Kali-linux Arpspoof工具
查看>>
PDF文档页面如何重新排版?
查看>>
基于http协议使用protobuf进行前后端交互
查看>>
bash腳本編程之三 条件判断及算数运算
查看>>
php cookie
查看>>
linux下redis安装
查看>>
弃 Java 而使用 Kotlin 的你后悔了吗?| kotlin将会是最好的开发语言
查看>>
JavaScript 数据类型
查看>>
量子通信和大数据最有市场突破前景
查看>>
对‘初学者应该选择哪种编程语言’的回答——计算机达人成长之路(38)
查看>>
如何申请开通微信多客服功能
查看>>
Sr_C++_Engineer_(LBS_Engine@Global Map Dept.)
查看>>
非监督学习算法:异常检测
查看>>
jquery的checkbox,radio,select等方法总结
查看>>
Linux coredump
查看>>