6.0权限的那些事
前言
随着Android版本的升级,Android系统也越来越完善,在Android 6.0版本(SdkVersion 23),Android为我们带来了全新的权限申请机制,对于部分高危权限应用必须动态申请才能继续操作,下面,我就来详细介绍下Android 6.0动态权限的那些事。
简单的说明
在Android 6.0之前:对于应用权限管理是在应用安装的时候以列表的形式展示给用户,用户选择安装则表示同意了这些权限,如果不同意就只能选择取消安装(部分国产ROM可以在这个阶段选择给与或者拒绝)。
这样就带来一个安全问题,如果用户没有详细看权限列表直接安装了,这样恶意应用就可能获取到很多危险权限。比如读取通讯录等。
Android 6.0及以后:如果你的应用targetSdkVersion < 23,那么权限管理机制与Android 6.0之前的操作一样,这样的目的是为了防止旧app在Android 6.0上面因为权限机制的变化崩溃。如果你的应用targetSdkVersion >= 23,那么你就必须动态申请高危权限,否则app会直接崩溃。
那么是不是只要在开发的时候将targetSdkVersion设置的比23小那么就一切ok呢?如果暂时还不想适配6.0运行时权限,但是又想要app可以在6.0及以上机型运行,那我们可以将目标版本改为23以下,如:targetSdkVersion 22。但是由于6.0机型在设置中可以进行权限管理,用户可以取消该应用的某个权限,但是app并不知道该权限被取消,此时app会崩溃(合理的try可以避免)。
权限的分类
Android 6.0将权限分为一般权限和危险权限两种,一般权限跟以前一样在AndroidManifest直接声明即可,危险权限需要开发者不仅需要声明而且在代码中还要动态申请权限。
Normal Permissions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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_INSTALL_PACKAGES SET_ALARM SET_TIME_ZONE SET_WALLPAPER SET_WALLPAPER_HINTS TRANSMIT_IR UNINSTALL_SHORTCUT USE_FINGERPRINT VIBRATE WAKE_LOCK WRITE_SYNC_SETTINGS |
Dangerous Permissions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
group:android.permission-group.CONTACTS permission:android.permission.WRITE_CONTACTS permission:android.permission.GET_ACCOUNTS permission:android.permission.READ_CONTACTS group:android.permission-group.PHONE permission:android.permission.READ_CALL_LOG permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.WRITE_CALL_LOG permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS permission:com.android.voicemail.permission.ADD_VOICEMAIL group:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDAR group:android.permission-group.CAMERA permission:android.permission.CAMERA group:android.permission-group.SENSORS permission:android.permission.BODY_SENSORS group:android.permission-group.LOCATION permission:android.permission.ACCESS_FINE_LOCATION permission:android.permission.ACCESS_COARSE_LOCATION group:android.permission-group.STORAGE permission:android.permission.READ_EXTERNAL_STORAGE permission:android.permission.WRITE_EXTERNAL_STORAGE group:android.permission-group.MICROPHONE permission:android.permission.RECORD_AUDIO group:android.permission-group.SMS permission:android.permission.READ_SMS permission:android.permission.RECEIVE_WAP_PUSH permission:android.permission.RECEIVE_MMS permission:android.permission.RECEIVE_SMS permission:android.permission.SEND_SMS permission:android.permission.READ_CELL_BROADCASTS |
看到上面的dangerous permissions,我们可以发现,危险权限都是一组一组的,如果app运行在Android 6.x的机器上,如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
相关API
检查是否拥有某权限:ActivityCompat.checkSelfPermission(@NonNull Context context, @NonNull String permission)
申请权限:ActivityCompat.requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final int requestCode)
申请权限结果回调:覆写Activity中的onRequestPermissionsResult方法。
1 2 3 |
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { } |
检查用户拒绝权限的时候是否点击了不在提醒:ActivityCompat.shouldShowRequestPermissionRationale(@NonNull Activity activity,@NonNull String permission)
最佳实战
doBackUp()是一个备份联系人的方法,由于读取联系人是高危权限,所以必须先验证是否拥有权限,如果没有权限则申请权限以后继续操作,当用户拒绝权限以后通过ActivityCompat.shouldShowRequestPermissionRationale方法验证是否选择了不再提示然互进行相应操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
private void doBackUp() { //如果拥有读取联系人权限 if (PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)) { mContactOperate.doBackup(ContactsUtil.queryAllContacts(getContentResolver())); } else { //申请读取联系人 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 10); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 10) { //用户授予了权限 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { mContactOperate.doBackup(ContactsUtil.queryAllContacts(getContentResolver())); } else { //权限被用户拒绝了,但是并没有选择不再提示,也就是说还可以继续申请 if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { //提示用户必须给与权限才能继续操作 Snackbar snackbar = Snackbar.make(mDrawerLayout, "需要授予权限才能继续操作", Snackbar.LENGTH_LONG); snackbar.setAction("重试", new View.OnClickListener() { @Override public void onClick(View v) { //继续申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS},10); } }); snackbar.show(); } else { //用户拒绝并选择了不在提示,这里可以提示用户然后打开权限设置界面 ToastUtil.show(this, R.string.no_permission_read_contacts); } } } } |
Leridy_Lei
安卓都已经出到 8 了。。。你还在讨论 6
Alias
@Leridy_Lei 你要知道目前8.0的市场占有率不到百分之一