Android UI相关

Android 运行时权限之EasyPermissions

2017-09-08  本文已影响554人  安静的学点东西

上一篇我们介绍了Android M之后系统引入了新的权限管理机制即运行时权限管理,但是真正的要在自己项目中引入该机制,需要编写很多额外的代码,让人望而生畏!为了解决这个问题,很多网友为了能简化代码、降低使用难度,封装了很多优秀的权限库,主要有RxPermissionsAndPermission和Google提供的easypermissions

今天我们主要介绍的就是Google为我们提供的EasyPermissions

相关API

函数
EasyPermissions.requestPermissions() 请求权限
EasyPermissions.hasPermissions() 是否拥有
EasyPermissions.onRequestPermissionsResult() 将权限处理结果托管给EasyPermission
EasyPermissions.PermissionCallbacks 权限结果处理回调
onPermissionsGranted() 返回获取成功的权限
onPermissionsDenied() 返回获取失败的权限
AfterPermissionGranted 当权限获取成功之后会回调到该注解方法中

使用

注意:该部分的内容主要是从官网翻译而来~

  1. 导入EasyPermissions

    dependencies {
        compile 'pub.devrel:easypermissions:1.0.0'
    }
    
  2. 请求权限

    下面的例子展示了在一个方法中如何同时去获取拍照和定位两个权限,有些知识点是你必须要注意:

    • 使用EasyPermissions#hasPermissions(...)去检查是否已经获取了需要申请的权限,该方法可以同时传多个权限作为参数;
    • 使用EasyPermissions#requestPermissions申请权限,该方法会申请系统权限并且如果需要的话会提示用户提供的申请权限的原因。传入的requestCode必须唯一,该方法同时可以传入多个权限作为参数。
    • 使用AfterPermissionGranted 注解,这是可选的,但是很便捷。只要所有的权限被允许,所有的被添加想通requestCode的注解的方法将会被执行。这种方法大大简化了只有所有权限都被允许时进行下一步操作的流程。你也可以实现onPermissionsGranted 接口对结果进行详细的处理。
    @AfterPermissionGranted(RC_CAMERA_AND_LOCATION)
    private void methodRequiresTwoPermission() {
        String[] perms = {Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION};
        if (EasyPermissions.hasPermissions(this, perms)) {
            // Already have permission, do the thing
            // ...
        } else {
            // Do not have permissions, request them now
            EasyPermissions.requestPermissions(this, getString(R.string.camera_and_location_rationale),
                    RC_CAMERA_AND_LOCATION, perms);
        }
    }
    
  3. 可选选项,为了更好的进行控制,你可以让你的Activity/Fragment实现PermissionCallBacks接口。

    public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            // 将处理结果托管给EasyPermissions进行处理
            EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
        }
    
        @Override
        public void onPermissionsGranted(int requestCode, List<String> list) {
            // Some permissions have been granted
            // ...
        }
    
        @Override
        public void onPermissionsDenied(int requestCode, List<String> list) {
            // Some permissions have been denied
            // ...
        }
    }
    
  4. 必要的权限

    有些时候,你的应用必须拥有某些必要的权限才能够运行,但是用户拒绝权限并且点击了"不再提醒"选项,你将不能够请求这些权限并且你必须去设置里面进行修改。对于上面的情况你可以调用EasyPermissions.somePermissionPermanentlyDenied(...)方法进行检查,然后弹出一个对话框提示用户去系统设置里面进行修改。

    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size());
    
        // (Optional) 检查用户拒绝权限的时候是否选择了“不再提醒”的情况
        // 这里将弹出对话框引导用户去系统设置
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            new AppSettingsDialog.Builder(this).build().show();
        }
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) {
            // 监听从设置页面返回,然后进行下一步处理,如弹出一个toast
            Toast.makeText(this, R.string.returned_from_app_settings_to_activity, Toast.LENGTH_SHORT)
                    .show();
        }
    }
    

    原理解析

    核心文件之EasyPermissions.java:

    • hasPermissions()

         /**
           * 检查是否有这一系列的权限
           *
           * @param context the calling context. 调用该方法的context
           * @param perms   一个或多个权限,比如:Manifest.permission.CAMERA
           * @return  如果所有的权限都被授予了,返回true;如果至少有一个权限没被授予,那么返回false;
           * @see Manifest.permission
           */
          public static boolean hasPermissions(@NonNull Context context, @NonNull String... perms) {
              //因为SDK版本如果小于M,那么会采用之前的权限管理机制,所以总是返回true,让系统自己去决定需要的权限。
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                  Log.w(TAG, "hasPermissions: API version < M, returning true by default");
                  // DANGER ZONE!!! Changing this will break the library.
                  return true;
              }
      
              for (String perm : perms) {
                  //这里最终还是调用ContextCompat.checkSelfPermission检查权限
                  boolean hasPerm = (ContextCompat.checkSelfPermission(context, perm) ==
                          PackageManager.PERMISSION_GRANTED);
                  if (!hasPerm) {
                     //遍历所申请的权限,只要有一个被拒绝就返回false;
                      return false;
                  }
              }
              //只有所有的权限都被允许才会返回true
              return true;
          }
      

    • requestPermissions()

          //因为requestPermissions被重载多次,我们只分析最终被调用的方法
          private static void requestPermissions(
                  @NonNull PermissionHelper helper, @NonNull String rationale,
                  @StringRes int positiveButton, @StringRes int negativeButton,
                  int requestCode, @NonNull String... perms) {
      
              // Check for permissions before dispatching the request
              if (hasPermissions(helper.getContext(), perms)) {
                 //这里会检查以下所有的权限,如果全部被允许就调用notifyAlreadyHasPermissions方法。
                  notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms);
                  //helper.getHost就是我们传入的Context或Activity或Fragment
                  return;
              }
      
              // Request permissions 请求权限
              helper.requestPermissions(rationale, positiveButton,
                      negativeButton, requestCode, perms);
          }
      
    • notifyAlreadyHasPermissions()

          /**
           * Run permission callbacks on an object that requested permissions but already has them by
           * simulating {@link PackageManager#PERMISSION_GRANTED}.
           *
           * @param object      the object requesting permissions.
           * @param requestCode the permission request code.
           * @param perms       a list of permissions requested.
           */
          private static void notifyAlreadyHasPermissions(@NonNull Object object,
                                                          int requestCode,
                                                          @NonNull String[] perms) {
              int[] grantResults = new int[perms.length];
              for (int i = 0; i < perms.length; i++) {
                  grantResults[i] = PackageManager.PERMISSION_GRANTED;
              }
             
              onRequestPermissionsResult(requestCode, perms, grantResults, object);
              //该方法会统计所有的权限的结果,将结果回调到UI中。
          }
      
    • onRequestPermissionsResult()

      public static void onRequestPermissionsResult(int requestCode,
                                                        @NonNull String[] permissions,
                                                        @NonNull int[] grantResults,
                                                        @NonNull Object... receivers) {
              // Make a collection of granted and denied permissions from the request.
              List<String> granted = new ArrayList<>();
              List<String> denied = new ArrayList<>();
              for (int i = 0; i < permissions.length; i++) {
                  String perm = permissions[i];
                  if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                      granted.add(perm);
                  } else {
                      denied.add(perm);
                  }
              }
      
              // iterate through all receivers
              for (Object object : receivers) {
                  // Report granted permissions, if any.
                  if (!granted.isEmpty()) {
                      if (object instanceof PermissionCallbacks) {
                         //将被用户同意的权限回调到UI中的onPermissionsGranted方法中
                          ((PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);
                      }
                  }
      
                  // Report denied permissions, if any.
                  if (!denied.isEmpty()) {
                      if (object instanceof PermissionCallbacks) {
                      ////将被用户拒绝的权限回调到UI中的onPermissionsDenied方法中
                          ((PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);
                      }
                  }
      
                  // If 100% successful, call annotated methods
                  if (!granted.isEmpty() && denied.isEmpty()) {
                     //当所有的权限都被同意的时候,调用runAnnotatedMethods方法通知requestcode相等且申明AfterPermissionGranted的方法去执行
                      runAnnotatedMethods(object, requestCode);
                  }
              }
          }
      
    • PermissionHelper中的requestPermissions()

          public void requestPermissions(@NonNull String rationale,
                                         @StringRes int positiveButton,
                                         @StringRes int negativeButton,
                                         int requestCode,
                                         @NonNull String... perms) {
              if (shouldShowRationale(perms)) {
              //如果存在其中一个权限是禁止且没有选择“不再询问”就会调用到这里,弹出一个对话框提示为什么要申请这些权限的原因。
                  showRequestPermissionRationale(
                          rationale, positiveButton, negativeButton, requestCode, perms);
              } else {
              //直接发出权限申请,首次申请或全部都被用户禁止的情况下才调用
                  directRequestPermissions(requestCode, perms);//这个方法其实调用的就是CompatContext.requestPermissions()方法
              }
          }
      

      代码太多了,建议对照源码进行学习,这里提供一个ER图给大家就会清晰明了。

easypermission_er.jpg
上一篇 下一篇

猜你喜欢

热点阅读