android 6.0 权限简述、思路分析和代码实践

android 6.0权限基本介绍

在6.0以前的版本,权限真的是一个巨恶心的东西,app 在使用过程中没有对权限正确的检查授权与否的方法和灵活的控制,这就非常不好了。让我想起之前做ocr的惨痛经历,因为ocr 需要调用摄像头对身份证进行扫描,而我却苦于无法知晓是否app对摄像头有使用权限,最终也是通过抓取异常啊之类的处理的。盼星星盼月亮google 终于舍得对android的权限做修改了。
(附一个链接,是 android 6.0以下判断是否有摄像头权限的)

Android 在6.0版本对权限做了比较大的改变,提供了一个新的权限模型 —运行时权限。

6.0权限和之前版本权限的区别:

  • 5.1及之前的版本。1.授权行为是在app安装的时候进行的,如果用户拒绝某个权限,那app也就不会进行下一步的安装过程;2.app使用过程中不能通过app直接对权限进行修改。
  • 6.0及以后的版本。1.授权行为可以发生在app运行时的任何地方,而不是像6.0之前的版本一样,只要用户不同意权限就不安装了;2.用户可以在使用过程中对app的权限请求选择授权或者拒绝授权。

系统权限分2种类型:正常权限(normal)危险权限(dangerous)。简单来说就是根据权限是否直接涉及到用户隐私。不涉及到用户隐私的当属于正常权限,反之则属于危险权限了。而危险权限又进行了一定的分组,权限和所属权限组以后也可能会变,下图是android 6.0 谷歌官网给出的危险权限 —— 权限组和权限。

android 权限组和权限

如果你的app 需要dangerous权限,你必须在每次执行这个功能的时候检查是否有此权限。用户总是可以自由地撤销权限,所以即使之前已经授权过的功能,同样不能保证这次使用的时候还具有权限。


android 6.0权限流程和思路

基本上是这样一个思路,举个例子说明:
条件:设备是Android 6.0 以上系统,并且app 的targetSdkVersion是23或者更高的版本
操作:点击Button 进入自定义拍照的界面。
流程分析:
当点击事件触发的时候,首先你需要先判断app 是否有这个“拍照和摄像”的权限,因为可能在这之前,用户关闭了app的“拍照和摄像”权限。如果有权限,就继续正常使用拍照和摄像的功能;如果没有权限,这个时候你app 就需要申请“拍照和摄像”权限了。在请求权限对话框,用户可以选择授权或者拒绝。同意授权:继续使用功能;拒绝:用户都拒绝了 肯定是不能在app上继续使用“拍照和摄像”功能了。如果用户已经拒绝了授权,而当用户再次点击Button使用“拍照和摄像权限”时,可能会需要给用户解释下 为什么app 的这个功能需要“拍照和摄像”权限之类的。
另外,这种做法是让app 对权限有了绝对的控制,还有一种做法是利用Intent跳转到相应的权限设置页面,让用户自行控制权限。
ok,下面附一张流程图,一下子就明白了。

android 6.0权限流程图

对上图做个简单的解释。圆角框标出的都是我们在实际开发中需要操作的部分。
黄色框在6.0里的方法是checkSelfPermission(),用于检查权限的。
绿色框“是否需要解释为什么申请此权限的原因”表示的是shouldShowRequestPermissionRationale()方法。
绿色框“申请摄像头的使用权限”表示requestPermissions()方法,用于申请权限的。
蓝色框表示无权限时的另一个方法。

继续使用上面的例子。例如,假设您的app需要能够使用相机拍照。

权限模式:您的app可以请求CAMERA 权限,它允许您的app直接访问相机。您的app将使用相机的APIs来控制相机,并拍照。这种方法让你的应用程序完全控制在拍照过程中,并且你能按照自己的需求去设计拍照的界面。

意图模式:使用一个意图ACTION_IMAGE_CAPTURE,直接调用系统的相机,进行拍照。当你发送意图后,系统提示用户选择一个相机应用程序(如果没有设置过默认的相机应用程序)。用户通过选择的拍照应用程序进行拍照,拍照后在你activity 的onactivityresult()方法里取图片。

两种方式同样都实现了拍照的功能,对比下这两种方式优缺点

权限模式:1.app可以实现对用户体验的完全控制,但是需要自己设计UI,使用起来比较复杂。2.无论是app在运行的时候还是在安装的时候(取决于用户的安卓版本),用户被都会被提示一次是否授予权限。只有成功授权后,你的app才可以使用其功能,而不需要额外的用户交互。但是,如果用户不授予权限(或在之后撤销权限),你的app将不能执行操作。

意图模式:1.不能很好的实现对用户体验的控制,使用系统UI,不需要自己设计UI,使用简单。2.不许要额外的权限(可选项)。需要Intent 实现跳转。

注意:不管是权限模式还是意图模式,使用功能前都需要先检查app是否具有权限。权限模式在国内的设备上很难调节成一致的,各大厂商对权限的都有自己的一套处理方法,像小米手机的权限请求就不会不出现google 系统给出的请求弹框,或者app内的权限设置和系统权限设置页面里不同步啊 种种问题,所以在国内还是不推崇使用权限模式,像微信在android 6.0上关于定位功能的实现方式就挺好,app不多做权限掺和,权限的授权与否用户自己设定,app只提供一个引导作用。

微信在android 6.0上的功能,如果检查到用户没有开启定位功能,就引导用户自己去定位服务设置界面自己设置,而不是微信请求定位服务的权限,让用户直接授权或者拒绝。


android 6.0 权限方法介绍

判断app是否有某个权限:checkSelfPermission(@NonNull Context context, @NonNull String permission),有此权限返回 PackageManager.PERMISSION_GRANTED,否则返回PackageManager.PERMISSION_DENIED

/**
 * Determine whether <em>you</em> have been granted a particular permission.
 * 
 * @param permission The name of the permission being checked.
 *
 * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
 * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
 *
 * @see android.content.pm.PackageManager#checkPermission(String, String)
 */
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }
    return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}

是否需要给用户解释:shouldShowRequestPermissionRationale(@NonNull Activity activity,@NonNull String permission)。当用户之前已经拒绝权限,或者没有权限时,需要向用户解释为什么需要权限或者其他提示等。方法返回 true:需要作出解释;false:不需要作出解释。

注:如果用户在之前的权限请求中拒绝授权,并在权限请求的系统对话框中,选择“ Don’t ask again(不在询问)”选项,此方法返回false。如果设备策略禁止应用程序具有该权限,该方法也将返回false。

/**
 *
 * @param activity The target activity.
 * @param permission A permission your app wants to request.
 * @return Whether you can show permission rationale UI.
 *
 * @see #checkSelfPermission(android.content.Context, String)
 * @see #requestPermissions(android.app.Activity, String[], int)
 */
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
        @NonNull String permission) {
    if (Build.VERSION.SDK_INT >= 23) {
        return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
    }
    return false;
}

请求授予app指定权限:requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final int requestCode)。如果app没有所需要的权限,需要调用此方法去请求相应的权限。这个是一个异步的方法,调用此方法时会弹出一个系统对话框,告诉用户需要请求的权限,用户可以选择授权或者拒绝授权。不管用户选择“授权”还是“拒绝”,系统都会调用回调方法,在相同的请求码中返回响应结果。另外,系统弹出的请求权限对话框是不能被修改的。

该系统所显示的对话框描述了您的应用程序需要访问的权限组;它没有列出具体的权限。例如,如果你需要READ_CONTACTS 权限,系统对话框只会说你的app需要访问该设备的联系人。用户只会为每个权限组授权一次。如果您的应用程序请求该组中的任何其他权限(即在您的应用程序清单中列出),系统会自动授予他们权限。当你请求权限的时候,如果用户明确的在系统对话框中进行了授权,系统会调用的 onRequestPermissionsResult() 回调方法,并且传递PERMISSION_GRANTED

注意:系统展示请求权限的对话框,app 不能对此进行配置和修改。如果需要提供其他的信息或解释给用户,应该在调用requestPermissions() 方法之前进行处理。如解释为什么app 需要这个权限

/**
* @param activity The target activity.
 * @param permissions The requested permissions.
 * @param requestCode Application specific request code to match with a result
 *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
 *    int, String[], int[])}.
    (这个参数是要和OnRequestPermissionsResult回调方法中的请求码相匹配使用的,类似于 startActivityForResult 和 onActivityResult 中的请求码requestCode,主要是用类区分多个请求的)
 *
 * @see #checkSelfPermission(android.content.Context, String)
 * @see #shouldShowRequestPermissionRationale(android.app.Activity, String)
 */
public static void requestPermissions(final @NonNull Activity activity,
        final @NonNull String[] permissions, final int requestCode) {
    if (Build.VERSION.SDK_INT >= 23) {
        ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
    } else if (activity instanceof OnRequestPermissionsResultCallback) {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                final int[] grantResults = new int[permissions.length];

                PackageManager packageManager = activity.getPackageManager();
                String packageName = activity.getPackageName();

                final int permissionCount = permissions.length;
                for (int i = 0; i < permissionCount; i++) {
                    grantResults[i] = packageManager.checkPermission(
                            permissions[i], packageName);
                }

                ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                        requestCode, permissions, grantResults);
            }
        });
    }
}

调用请求权限的方法后,响应用户操作的回调onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults)。当你的app 调用requestPermissions()方法去请求某个权限时,系统会显示出一个对话框给用户,当用户做出响应后, 系统会调用app 的 onRequestPermissionsResult()方法来传递用户的相应。你的app 必须重写这个方法来确认请求的权限是否被授权。这个回调是你传递了之前调用的requestPermissions() 方法时所用的请求码。每次调用requestPermissions()方法后都会调用此回调,需要注意一点的是,当与用户的请求权限交互被中断,这种情况下会返回一个空的权限和结果集,意味着被取消。

/**
 * Callback for the result from requesting permissions. This method
 * is invoked for every call on {@link #requestPermissions(String[], int)}.
 * <p>
 * <strong>Note:</strong> It is possible that the permissions request interaction
 * with the user is interrupted. In this case you will receive empty permissions
 * and results arrays which should be treated as a cancellation.
 * </p>
 *
 * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
 * @param permissions The requested permissions. Never null.
 * @param grantResults The grant results for the corresponding permissions
 *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
 *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
(授权结果只能是ackageManager.PERMISSION_GRANTED 或者PackageManager.PERMISSION_DENIED,从不为null,所以在重写这个方法时,也就省去了做非空判断了)
 *
 * @see #requestPermissions(String[], int)
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

android 6.0 权限使用

权限请求

    public void onByPressed(View view) {
        if (Build.VERSION.SDK_INT >= 23) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                /**
                 * 这里权限模式
                 */
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
                    // 解释为什么需要定位权限之类的
                } else {
                    // 请求权限处理
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
                }
                /**
                 * 如果要用意图模式的话,就不需要用权限模式了,直接跳转到系统设置页面,
                 * 让用户自己控制权限的授权与否,app只承担了一个引导作用
                 */
            } else {
                // 获得定位信息的code
            }
        } else {
            ToastTool.showToast(this, "6.0 以下");
        }
    }

用户响应权限请求的回调

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1: {
                // 如果授权被取消,结果数组是空的,注意这里是empty ,而不是null
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // 已经授权,在这里做你想要做的事
                } else {
                    // 拒绝授权
                }
                return;
            }
            // 其他的权限请求处理
        }
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值