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
危险权限
危险权限被分为了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);}
处理用户响应
重写 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
- Android6.0 运行时权限简单理解 -- Android学习之路
- Android6.0 运行时权限简单理解 -- Android学习之路
- Android学习笔记-Android6.0运行时权限
- Android学习笔记-Android6.0运行时权限(续)
- ionic学习之Android6.0 运行时权限插件cordova-plugin-android-permissions
- Android6.0运行时权限学习总结
- Android6.0运行时权限学习
- Android6.0运行时权限学习见解
- Android6.0之运行时权限
- Android6.0运行时权限的理解和封装
- Android6.0 运行时权限
- Android6.0 运行时权限
- Android6.0运行时权限
- 【Android6.0】运行时权限
- Android6.0 运行时权限
- Android6.0 运行时权限
- Android6.0 运行时权限
- Android6.0运行时权限
- C语言学习要点
- Http 与 Socket 区别
- iOS几种不同的键盘布局
- MFC对多线程编程的支持
- 软件架构师常会用到的几款软件
- Android6.0 运行时权限简单理解 -- Android学习之路
- AndroidAutoLayout使用详解
- android 图片高斯模糊
- java中this关键字用法小结
- com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after co
- 反转单链表的几种方法
- 从 inode 了解 Linux 文件系统
- AOP学习笔记
- html5布局适配rem