Android开发笔记-权限管理
Android 6.0对用户权限管理机制进行了改善。不再像之前一样在首次安装应用时处理权限请求,现在的权限设置只有在应用需要使用该功能的时候才会申请该权限,也就是我们所说的运行时权限。
如果App暂未支持运行时权限,最好将targetSdkVersion设置为23以下,系统将会以旧的权限管理系统来处理,否则在Android 6.0系统上运行时可能会出问题。
权限分组
Android开发中,在使用某些特定功能时需要申请权限,但并非所以权限都是敏感权限,因此系统对权限进行了分类:
- 正常权限(Normal Permissions)
- 危险权限(Dangerous Permissions)
- 特殊权限(Particular Permissions)
- 其他权限(一般很少用到)
正常权限(Normal Permissions)
-
特点: 对用户隐私或设备操作没有较大影响。
-
申请方式: 直接在AndroidManifest.xml文件中声明。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.myapp" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> ... </manifest>
-
管理方式: 系统将自动为App赋予这些权限,不需要显示的提醒用户。
-
权限列表:所涉及到的基本是关于网络、蓝牙、时区、快捷方式等方面。
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 INTERNET KILL_BACKGROUND_PROCESSES MODIFY_AUDIO_SETTINGS NFC READ_SYNC_SETTINGS READ_SYNC_STATS RECEIVE_BOOT_COMPLETED REORDER_TASKS REQUEST_INSTALL_PACKAGES SET_TIME_ZONE SET_WALLPAPER SET_WALLPAPER_HINTS TRANSMIT_IR USE_FINGERPRINT VIBRATE WAKE_LOCK WRITE_SYNC_SETTINGS SET_ALARM INSTALL_SHORTCUT UNINSTALL_SHORTCUT
危险权限(Dangerous Permissions)
-
特点: 可能会对用户隐私或设备的正常操作造成影响。是运行时权限主要处理的对象。
-
申请方式: 当App运行时需要该权限时申请。每次需要权限是都要检查是否有被授予,如果没有则需要询问用户。
private static final int REQUEST_READ_CONTACTS_PERMISSION = 22; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // 权限请求回调 switch (requestCode) { case REQUEST_READ_CONTACTS_PERMISSION: // 读取联系人权限成功获取 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) queryContacts(); else Toast.makeText(this, "没有读取联系人权限", Toast.LENGTH_SHORT).show(); break; } } public boolean checkPermission(String permission) { // 判断是否有权限 int permissionCheck = ContextCompat.checkSelfPermission( this, permission); if (permissionCheck != PackageManager.PERMISSION_GRANTED) { // 没有获取权限 // 判断是否应该说明申请权限的理由 boolean shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.READ_CONTACTS); Log.i(TAG, "rationale: " + shouldShowRationale); if (shouldShowRationale) { // 说明理由,等待用户反馈,再申请权限 } // 发起申请权限请求 ActivityCompat.requestPermissions(this, new String[]{permission}, REQUEST_READ_CONTACTS_PERMISSION); } return permissionCheck == PackageManager.PERMISSION_GRANTED; } public void getContacts(View view) { if (Build.VERSION.SDK_INT < 23 || checkPermission(Manifest.permission.READ_CONTACTS)) queryContacts(); } public void queryContacts() { Cursor cursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME}, null, null, ContactsContract.Contacts.SORT_KEY_PRIMARY); if (cursor == null) return; while (cursor.moveToNext()) { Log.i(TAG, "id: " + cursor.getString(0) + ", name: " + cursor.getString(1)); } cursor.close(); }
-
注意:
-
当程序支持Android 6.0时,也要兼容以前的版本。
-
Fragment中申请权限时,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult
-
(此问题23.3版本库已修复)
如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方法,onRequestPermissionsResult不会回调回来,建议使用getParentFragment().requestPermissions方法,
这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragment fragment : fragments) { if (fragment != null) fragment.onRequestPermissionsResult(requestCode,permissions,grantResults); } } }
-
-
管理方式: 系统需要用户显示的授予这些权限,且用户随时可以撤回授予的权限。
-
分组: 系统对权限进行了分组(Group),属于同一组的权限互相绑定,一旦组内某个权限被允许,该组的其他权限也将允许。
-
权限列表:
特殊权限(Particular Permissions)
-
特点: 对系统来说是特别敏感的权限。
-
权限列表:
SYSTEM_ALERT_WINDOW // 设置悬浮窗,进行一些黑科技 WRITE_SETTINGS // 修改系统设置
-
申请方式:在需要使用时,使用startActivityForResult启动授权界面来完成。