Android开发笔记-权限管理

Android 6.0对用户权限管理机制进行了改善。不再像之前一样在首次安装应用时处理权限请求,现在的权限设置只有在应用需要使用该功能的时候才会申请该权限,也就是我们所说的运行时权限。

如果App暂未支持运行时权限,最好将targetSdkVersion设置为23以下,系统将会以旧的权限管理系统来处理,否则在Android 6.0系统上运行时可能会出问题。

权限分组


Android开发中,在使用某些特定功能时需要申请权限,但并非所以权限都是敏感权限,因此系统对权限进行了分类:

  • 正常权限(Normal Permissions)
  • 危险权限(Dangerous Permissions)
  • 特殊权限(Particular Permissions)
  • 其他权限(一般很少用到)

正常权限(Normal Permissions)


  • 特点: 对用户隐私或设备操作没有较大影响。

  • 申请方式: 直接在AndroidManifest.xml文件中声明。

      
    
          
          ...
      
    
  • 管理方式: 系统将自动为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();
      }
    
  • 注意:

    1. 当程序支持Android 6.0时,也要兼容以前的版本。

    2. Fragment中申请权限时,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult

    3. (此问题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 fragments = getChildFragmentManager().getFragments();
           if (fragments != null) {
               for (Fragment fragment : fragments) {
                   if (fragment != null)
                       fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
               }
           }
       }
      
  • 管理方式: 系统需要用户显示的授予这些权限,且用户随时可以撤回授予的权限。

  • 分组: 系统对权限进行了分组(Group),属于同一组的权限互相绑定,一旦组内某个权限被允许,该组的其他权限也将允许。

  • 权限列表:


    Android开发笔记-权限管理_第1张图片

特殊权限(Particular Permissions)


  • 特点: 对系统来说是特别敏感的权限。

  • 权限列表:

      SYSTEM_ALERT_WINDOW   // 设置悬浮窗,进行一些黑科技
      WRITE_SETTINGS   // 修改系统设置        
    
  • 申请方式:在需要使用时,使用startActivityForResult启动授权界面来完成。

相关开源框架


  • PermissionsDispatcher

  • RxPermissions

  • Grant

  • android-RuntimePermissions

参考文献


  • System Permissions

  • Requesting Permissions at Run Time

  • 聊一聊Android 6.0的运行时权限

  • Android 6.0 运行时权限处理

你可能感兴趣的:(Android开发笔记-权限管理)