在Android 6.0 设备上动态获取权限

    |     2016年6月2日   |   Android技术   |     0 条评论   |    1634

Android 6.0(API 23)起,危险权限改成运行时授权:安装时不再一揽子通过,真正用到电话、短信、相机时才弹窗。本篇用拨号(CALL_PHONE)走一遍检查、申请和回调。


一、先写出拨号

20140731144525904006

拨号本身还是一个 Intent。注意是 ACTION_CALL(直接拨出),不是打开拨号盘的 ACTION_DIAL:

private void callDirectly(String mobile){
        Intent intent = new Intent();
        intent.setAction("android.intent.action.CALL");
        intent.setData(Uri.parse("tel:" + mobile));
        mContext.startActivity(intent);
    }

Manifest 里仍要声明权限,运行时申请并不能代替这一行:

<uses-permission android:name="android.permission.CALL_PHONE" />

二、为什么在 6.0 上会崩溃

Android 6.0 之前,上面代码没问题。若设备已是 6.0,且 targetSdkVersion 设为 23,一点拨号就会崩。

把 targetSdkVersion 降到 23 以下,程序不会崩,但在 6.0 设备上依旧拨不了电话。权限机制是 Android M 才加入的:从 M 开始,声明过的权限要在运行时动态授予。要兼容 6.0,必须在代码里检查并申请,而不是只改 target。

动态分配细节可看原文 权限动态分配;只想看做法可跳到 解决方案。


三、安装时授权 vs 运行时授权

6.0 之前,安装界面会列出应用索要的全部权限:

permission

上图分别是 Nexus 6 和小米在安装时的界面。点「接受 / 安装」就等于一次授权全部声明权限。很少有人会因为某一条敏感权限而放弃安装——那时用户还没真正去读通话记录或短信。

更合理的时机是:应用马上要读短信时,系统弹出提示,用户结合当前场景决定给不给。不给会怎样、给了会怎样,都由用户主动判断。这比安装时那一勾更尊重隐私。

MIUI 很早就做了运行时授权,比官方 Android 更早落地。

直到 Android 6.0,Google 才把这套做成系统能力:安装时仍提示会用到哪些权限;真正获取敏感权限时再弹窗。例如读短信:

permission

官方说明大意:从 Android 6.0(API 23)起,用户在应用运行过程中授权,而不是安装时一次性授予。安装更轻,用户也能按功能拆开权限——比如只给相机、不给定位,随时可在系统设置里收回。

对比 Android 6.0 之前 Android 6.0(target 23)
授权时机 安装时一次性同意全部 用到危险权限时再弹窗
只声明不申请 一般可直接调用 可能崩溃或功能被拒
用户反悔 卸载或改系统设置(因 ROM 而异) 设置里可随时撤销
取巧降 target 无此问题 不崩,但拨号等能力仍可能不可用

四、检查、申请、回调

对版本做判断,6.0 走兼容分支:

final public static int REQUEST_CODE_ASK_CALL_PHONE = 123;

 public void onCall(String mobile){
        this.mMobile = mobile;
        if (Build.VERSION.SDK_INT >= 23) {
            int checkCallPhonePermission = ContextCompat.checkSelfPermission(mContext,Manifest.permission.CALL_PHONE);
            if(checkCallPhonePermission != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(mContext,new String[]{Manifest.permission.CALL_PHONE},REQUEST_CODE_ASK_CALL_PHONE);
                return;
            }else{
                //上面已经写好的拨号方法
                callDirectly(mobile);
            }
        } else {
            //上面已经写好的拨号方法
            callDirectly(mobile);
        }
    }

6.0 用户触发拨号时,会看到 Material 风格的系统对话框:

permission

requestPermissions 的最后一个参数是 requestCode,类似 onActivityResult。申请之后要在当前 Activity 实现 onRequestPermissionsResult:

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_CALL_PHONE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission Granted
                    callDirectly(mobile);
                } else {
                    // Permission Denied
                    Toast.makeText(MainActivity.this, "CALL_PHONE Denied", Toast.LENGTH_SHORT)
                            .show();
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

用 grantResults[0] 判断用户点了允许还是拒绝,再走拨号或提示。


五、有用的链接

上面只是拨号这一条权限的最小示例。更完整的运行时权限说明见:

出自 咕咚 的博客:在Android 6.0 设备上动态获取权限

一句话总结:target 23 之后危险权限要 checkSelfPermission → requestPermissions → onRequestPermissionsResult,只降 target 不崩也拨不了电话。

转载请注明来源:在Android 6.0 设备上动态获取权限
本文链接地址:https://ai.zhousir.top/?p=1598
回复 取消