Android6.0 运行时权限简单理解 -- Android学习之路

来源:互联网 发布:网络中fr是什么意思 编辑:程序博客网 时间:2024/06/16 23:55

sky-mxc 总结 转载注明出处:https://sky-mxc.github.io

6.0 运行时权限处理

在6.0以前 权限都是在安装时授权的,如果用户不授权就无法安装;
Android从6.0(API 23)开始 使用运行时权限,而不是像以前那样安装时授权。当你需要某些权限时,系统会向用户去申请权限。用户可以随时取消授权给你的权限。
6.0中权限分为两类 普通权限和危险权限,普通权限在AndroidManifest 文件中注册就可以得到,对于能获得用户隐私的权限属于危险权限。在使用的时候必须用户授权才能使用。例如 拍照,录音 sd卡的操作,危险权限被分为很多组,只要一组中的其中一项被授权 Android 就会将这一组的权限打包都授权给你app

image

危险权限

危险权限被分为了9组

    Permission Group    Permissions    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

普通权限

    • ACCESS_LOCATION_EXTRA_COMMANDS      • ACCESS_NETWORK_STATE    • ACCESS_NOTIFICATION_POLICY    • ACCESS_WIFI_STATE    • BLUETOOTH    • BLUETOOTH_ADMIN    • BROADCAST_STICKY    • CHANGE_NETWORK_STATE    • CHANGE_WIFI_MULTICAST_STATE    • CHANGE_WIFI_STATE    • DISABLE_KEYGUARD    • EXPAND_STATUS_BAR    • GET_PACKAGE_SIZE    • INSTALL_SHORTCUT    • INTERNET    • KILL_BACKGROUND_PROCESSES    • MODIFY_AUDIO_SETTINGS    • NFC    • READ_SYNC_SETTINGS    • READ_SYNC_STATS    • RECEIVE_BOOT_COMPLETED    • REORDER_TASKS    • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS    • REQUEST_INSTALL_PACKAGES    • SET_ALARM    • SET_TIME_ZONE    • SET_WALLPAPER    • SET_WALLPAPER_HINTS    • TRANSMIT_IR    • UNINSTALL_SHORTCUT    • USE_FINGERPRINT    • VIBRATE    • WAKE_LOCK        • WRITE_SYNC_SETTINGS

请求权限

targetSdkVerion

申请权限之前必须先说一下tartgetSdkVersion ,目标sdk版本,一般定义在build.gradle文件中。
如果 targetSDKVersion 是22 安装好之后 Android系统就知道这个App在系统API22一下都测试过了并且能正确运行的,假如这个App运行在了Android6.0系统上,Android就会对这个App很”照顾“,兼容它正确运行。6.0系统会把App申请的权限都默认给这个App。
但是 ,在6.0系统 ,用户可随时撤销授权给app的权限 ,即使系统默认都授权给你,用户也可以取消掉。这时就没权限了。所以即使是targetSDKVersion < 23 也不是就万事大吉了。Android为我们提供了android.support.v4.content.PermissionChecker 来检测是否具有某些权限

判断 targetSdkVersion

/** * 检查targetSDKVersion 是否在 23以上 * @return */private boolean checkTargetSdkVersion(){    PackageInfo info= null;    try {        info = getPackageManager().getPackageInfo(getPackageName(),0);    } catch (PackageManager.NameNotFoundException e) {        e.printStackTrace();    }    int targetSdk=  info.applicationInfo.targetSdkVersion;    log("TargetSdkVersion:"+targetSdk);    if (targetSdk>=Build.VERSION_CODES.M){        return true;    }    return false;}

检查权限

在去请求权限之前 应该先检查一下系统 的版本 如果系统版本在6.0以上再去请求权限,如果不在就不去请求,直接使用

/** * 检查系统版本是否在6.0或者6.0以上 * @return  */private boolean checkVersion(){    // Build.VERSION.SDK_INT 当前系统版本    //Build.VERSION_CODES.M 6.0版本    if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.M){        return true;    }    return false;}

PermissionChecker.checkSelfPermission() 方法就是用于检查App自身有没有某一个权限 此方法适用于 targetSdkVersion < 23

context.checkSelfPermission() 适用于 targetSDKVersion >=23

返回结果有三种 状态
- PermissionChecker.PERMISSION_GRANTED; //有权限
- PermissionChecker.PERMISSION_DENIED ; //无权限
- PermissionChecker.PERMISSION_DENIED_APP_OP;//无权限

PermissionChecker.PERMISSION_DENIED 和 PermissionChecker.PERMISSION_DENIED_APP_OP 的区别:
- targetSDKVersion 小于23没有权限就返回 PermissionChecker.PERMISSION_DENIED_APP_OP
- targetSdkVersion23或者以上的返回 PermissionChecker.PERMISSION_DENIED

java
//检测targetSDKVersion 是否在23以上
if (checkTargetSdkVersion()){
//targetSDKVersion >=23
//检查是否具有读取短信的权限
result = checkSelfPermission(permission);
}else{
//targetSDKVersion <23
//检查是否具有读取短信的权限
result= PermissionChecker.checkSelfPermission(this,permission);
}

请求权限

使用 requestPermissions() 方法去请求权限 参数有两个 权限数组 和请求码

requestPermissions(new String[]{"android.permission.READ_SMS"},10);

在请求权限之前最好是跟用户解释清楚为什么要使用这个权限 ,用时候用户并不清楚为什么使用权限 就会被拒绝,如果一个权限被请求一次以上 在系统申请权限的Dialog会出现一个不再提醒的复选框 那怎么判断 用户是否勾选了这个 不再提醒呢 ,Android提供了 shouldShowRequestPermissionRationale() 方法;

这个方法 在 第一次请求的时候 和 在用户勾选了不再提醒时 返回false ,其他均返回true

// 第一次请求就返回false 拒绝过返回true 或者 用户选择不再提示返回falseboolean answer=  shouldShowRequestPermissionRationale(permission);log("shouldShowRequestPermissionRationale :"+answer);if (!answer){    new AlertDialog.Builder(this).setTitle("权限说明")            .setMessage("此功能需要读取短信的权限,没有权限无法使用此功能。请在稍后授权后使用")            .setNegativeButton("确定", new DialogInterface.OnClickListener() {                @RequiresApi(api = Build.VERSION_CODES.M)                @Override                public void onClick(DialogInterface dialog, int which) {                    requestPermissions(new String[]{permission},SMS);                }            })            .setNeutralButton("取消",null)            .show();}else{    requestPermissions(new String[]{permission},SMS);}

image


处理用户响应

重写 activity的 onRequestPermissionsResult() 的方法 处理权限的响应

权限的申请是可以多个权限一块申请的 ,所以 响应结果也是 数组和 请求的权限数组对应

/** *  申请权限的响应 * @param requestCode 请求码 * @param permissions 权限数组 * @param grantResults 结果数组 */@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    switch (requestCode){        case SMS:          LogCheckResult(grantResults[0]);            if (grantResults.length>0 && grantResults[0]==PermissionChecker.PERMISSION_GRANTED){                //TODO 读取短信                Toast.makeText(this,"读取短信授权成功",Toast.LENGTH_SHORT).show();                tv.setText(getSmsInPhone());            }else{                Toast.makeText(this,"读取短信授权失败",Toast.LENGTH_SHORT).show();            }            break;    }}

完整的短信读取权限申请 流程

请求权限

/** * 请求短信权限 */@RequiresApi(api = Build.VERSION_CODES.M)private void requestSms() {    // 权限        final String permission = "android.permission.READ_SMS";    //检查当前系统版本是否在6.0以上    if (checkVersion()){        int result =-1;        //检测targetSDKVersion 是否在23以上            if (checkTargetSdkVersion()){                //targetSDKVersion >=23                //检查是否具有读取短信的权限                result = checkSelfPermission(permission);            }else{                //targetSDKVersion <23                //检查是否具有读取短信的权限                result= PermissionChecker.checkSelfPermission(this,permission);            }            LogCheckResult(result);            if(result==PermissionChecker.PERMISSION_GRANTED){                //已经有了权限                //TODO 读取短信                Toast.makeText(this,"读取短信授权成功",Toast.LENGTH_SHORT).show();                tv.setText(getSmsInPhone());            }else{                //没有权限                //TODO 请求权限                // 第一次请求就返回false 拒绝过返回true 或者 用户选择不再提示返回false              boolean answer=  shouldShowRequestPermissionRationale(permission);                log("shouldShowRequestPermissionRationale :"+answer);                if (!answer){                    new AlertDialog.Builder(this).setTitle("权限说明")                            .setMessage("此功能需要读取短信的权限,没有权限无法使用此功能。请在稍后授权后使用")                            .setNegativeButton("确定", new DialogInterface.OnClickListener() {                                @RequiresApi(api = Build.VERSION_CODES.M)                                @Override                                public void onClick(DialogInterface dialog, int which) {                                    requestPermissions(new String[]{permission},SMS);                                }                            })                            .setNeutralButton("取消",null)                            .show();                }else{                    requestPermissions(new String[]{permission},SMS);                }            }    }else{        //无需请求        Toast.makeText(this,"读取短信授权成功",Toast.LENGTH_SHORT).show();        tv.setText(getSmsInPhone());    }}

响应处理

@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    switch (requestCode){        case SMS:          LogCheckResult(grantResults[0]);            if (grantResults.length>0 && grantResults[0]==PermissionChecker.PERMISSION_GRANTED){                //TODO 读取短信                Toast.makeText(this,"读取短信授权成功",Toast.LENGTH_SHORT).show();                tv.setText(getSmsInPhone());            }else{                Toast.makeText(this,"读取短信授权失败",Toast.LENGTH_SHORT).show();            }            break;    }}

短信读取 代码

    public String getSmsInPhone() {        log("开始读取短信");        final String SMS_URI_ALL = "content://sms/";        final String SMS_URI_INBOX = "content://sms/inbox";        final String SMS_URI_SEND = "content://sms/sent";        final String SMS_URI_DRAFT = "content://sms/draft";        final String SMS_URI_OUTBOX = "content://sms/outbox";        final String SMS_URI_FAILED = "content://sms/failed";        final String SMS_URI_QUEUED = "content://sms/queued";        StringBuilder smsBuilder = new StringBuilder();        try {            Uri uri = Uri.parse(SMS_URI_ALL);            String[] projection = new String[] { "_id", "address", "person", "body", "date", "type" };            Cursor cur = getContentResolver().query(uri, projection, null, null, "date desc");      // 获取手机内部短信            log("cursor:"+cur.getCount());            if (cur.moveToFirst()) {                int index_Address = cur.getColumnIndex("address");                int index_Person = cur.getColumnIndex("person");                int index_Body = cur.getColumnIndex("body");                int index_Date = cur.getColumnIndex("date");                int index_Type = cur.getColumnIndex("type");                do {                    String strAddress = cur.getString(index_Address);                    int intPerson = cur.getInt(index_Person);                    String strbody = cur.getString(index_Body);                    long longDate = cur.getLong(index_Date);                    int intType = cur.getInt(index_Type);                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");                    Date d = new Date(longDate);                    String strDate = dateFormat.format(d);                    String strType = "";                    if (intType == 1) {                        strType = "接收";                    } else if (intType == 2) {                        strType = "发送";                    } else {                        strType = "null";                    }                    smsBuilder.append("[ ");                    smsBuilder.append(strAddress + ", ");                    smsBuilder.append(intPerson + ", ");                    smsBuilder.append(strbody + ", ");                    smsBuilder.append(strDate + ", ");                    smsBuilder.append(strType);                    smsBuilder.append(" ]\n\n");                } while (cur.moveToNext());                if (!cur.isClosed()) {                    cur.close();                    cur = null;                }            } else {                smsBuilder.append("no result!");            } // end if            smsBuilder.append("getSmsInPhone has executed!");        } catch (SQLiteException ex) {            log("SQLiteException in getSmsInPhone");        }        return smsBuilder.toString();    }

读取短信的代码参考这位大神的代码:http://blog.csdn.net/ithomer/article/details/7328321

关于这次的Demo,github 地址: https://github.com/sky-mxc/AndroidDemo/tree/master/permission

0 0
原创粉丝点击