RxPermissions源码解析

lifttime1983 7年前
   <h3>1.简介</h3>    <p>RxPermissions 是基于 RxJava 开发的用于帮助在 Android 6.0 中处理运行时权限检测的框架。在 Android 6.0 中,系统新增了部分权限的运行时动态获取。而不再是在以前的版本中安装的时候授予权限。</p>    <p>对于运行时的权限获取提示,国内的 Android 工程师们应该并不陌生,国内的第三方 ROM 例如 MIUI 在很早前就做了类似的功能。但是第三方 ROM 并不能提供给我们权限请求成功或失败的接口,这就导致我们无法通过 PackageManager 提供的 checkPermission() 方法来准确的获取到我们是否获得该权限。只能根据具体的权限来做相应的处理。但是在 Android 6.0 中我们可以准确的获取我们的应用是否获取某个权限,大致方法是通过 API 23 中的 Activity 的 requestPermissions(String[] permissions, int requestCode); 方法请求权限,并在 onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) 回调方法中处理请求结果。这个方法与 onActivityResult() 类似。 RxPermissions 在这个基础上做了封装,使我们在处理运行时权限变得更加的简单。</p>    <p>另外关于 RxJava 如果你现在还没了解过 RxJava 可以直接翻到文章最下面去查看我总结的一些 RxJava 相关的文章,不然并不推荐直接看这篇文章。下面我们就来具体看看 RxPermissions 的使用方法以及源码分析。</p>    <h3>2.使用方法</h3>    <p>1.直接获取权限(使用Retrolambda使代码更加简洁,当然并不是必须使用):</p>    <pre>  <code class="language-java">// 必须在初始化阶段调用,例如onCreate()方法中  RxPermissions.getInstance(this)      .request(Manifest.permission.CAMERA)      .subscribe(granted -> {          if (granted) { // 在android 6.0之前会默认返回true             // 已经获取权限          } else {             // 未获取权限          }      });</code></pre>    <p>2.通过条件触发获取权限(结合RxBinding使用)</p>    <pre>  <code class="language-java">// 必须在初始化阶段调用,例如onCreate()方法中  RxView.clicks(findViewById(R.id.enableCamera))      .compose(RxPermissions.getInstance(this).ensure(Manifest.permission.CAMERA))      .subscribe(granted -> {          // 当R.id.enableCamera被点击的时候触发获取权限      });</code></pre>    <p>3.一次请求多个权限(有两种方式)</p>    <p>如果同时请求多个权限,下面这种方式会合并请求结果,即所有权限请求成功会返回 true ,若有一个权限未成功则返回 false 。</p>    <pre>  <code class="language-java">RxPermissions.getInstance(this)      .request(Manifest.permission.CAMERA,               Manifest.permission.READ_PHONE_STATE)      .subscribe(granted -> {          if (granted) {             // 所有权限请求被同意          } else {             // 至少有一个权限没同意          }      });</code></pre>    <p>当然你可以通过 requestEach or ensureEach 来分别获取每一个权限请求的结果。</p>    <pre>  <code class="language-java">RxPermissions.getInstance(this)      .requestEach(Manifest.permission.CAMERA,               Manifest.permission.READ_PHONE_STATE)      .subscribe(permission -> { // 会发送两个Permission对象          if (permission.granted) {             // `permission.name` is granted !          }      });</code></pre>    <p>注意:由于在请求权限的过程中 app 有可能会被重启,所以权限请求必须放在初始化的阶段,比如在 Activity.onCreate/onResume , 或者</p>    <p>View.onFinishInflate 方法中。如果不这样处理,那么如果 app 在请求过程中重启的话,权限请求结果将不会发送给订阅者即 subscriber 。</p>    <h3>2.整体介绍</h3>    <p>RxPermission 一共就只有三个类: Permission 是定义的权限 model 类,用来存放权限名称以及是否获取权限的信息。 RxPermissions 就是最主要的类了,利用 RxJava 提供了我们上面在使用方法中介绍的所有方法。还有一个 ShadowActivity 类是用来请求权限用的。下面我们就来详细介绍 RxPermission 的实现。</p>    <p>注意:如果你还未了解过 RxJava 那么可以先阅读本文最后的一系列优秀文章。如果你已经了解过 RxJava 的话,那么下面我将会介绍 RxJava 中的部分操作符在 RxPermission 中的实际运用。相信能帮助你更好的理解 RxJava 中操作符的使用。</p>    <h3>3.源码分析</h3>    <p>我们依然按照我们惯用的方法来分析,通过使用方法来分析调用流程,最终理解整个项目。首先再回顾一遍使用方法(注意这里我同时请求了两个权限),那么结果将是如果所有权限请求成功会返回 true ,若有一个权限未成功则返回 false 。代码如下:</p>    <pre>  <code class="language-java">// 必须在初始化阶段调用,例如onCreate()方法中  RxPermissions.getInstance(this)      .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)      .subscribe(granted -> {          if (granted) { // 在android 6.0之前会默认返回true             // 已经获取权限          } else {             // 未获取权限          }      });</code></pre>    <p>1.RxPermissions.getInstance(this)的实现</p>    <pre>  <code class="language-java">static RxPermissions sSingleton;      private Context :;        public static RxPermissions getInstance(Context) {          if (sSingleton == null) {              sSingleton = new RxPermissions(ctx.getApplicationContext());          }          return sSingleton;      }        RxPermissions(Context ctx) {          mCtx = ctx;      }</code></pre>    <p>很明显是维护 RxPermissions 的单例,不再多做介绍,紧接着我们来看 RxPermissions 中 request(Manifest.permission.CAMERA) 方法的实现:</p>    <p>2.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)的实现:</p>    <pre>  <code class="language-java">public Observable<Boolean> request(final String... permissions) {          return Observable.just(null).compose(ensure(permissions));      }</code></pre>    <p>首先从返回值看到是返回一个 Observable<Boolean> 对象,方法中也是直接 return 了 Observable.just(null).compose(ensure(permissions)) 。这里涉及两个方法分别是 just() 以及 compose() 我们先解释这两个操作符:</p>    <p>1.Observable.just(null)</p>    <p>just 操作符可以将某个对象转化为 Observable 对象,并且将其发射出去,可以是一个数字、一个字符串、数组、Iterate对象等,是 RxJava 中非常快捷的创建 Observable 对象的方法。在这里 just() 方法中虽然传入的是 null 但是并不影响创建出的 Observable 的作用,如果有 subscriber 订阅依然会依次调用其 onNext() 和 onCompleted() 方法。所以这里就是为了创建出一个 Observable 对象,便于后续的处理。创建完 Observable 对象之后紧接着调用了 compose() 方法:</p>    <p>2.compose(Transformer)操作符</p>    <p>compose() 操作符是针对 Observable 自身的变换,通过我们自己定义的 Transformer 对象可以将对 Observable 对象变换的操作封装起来,实现一个简单的 Transformer 对象如下:</p>    <pre>  <code class="language-java">class myTransformer implements Observable.Transformer<Object, Boolean> {            @Override          public Observable<Boolean> call(Observable<Object> objectObservable) {              return objectObservable.map(new Func1<Object, Boolean>() {                  @Override                  public Boolean call(Object o) {                      return true;                  }              });          }      }</code></pre>    <p>通过上面这个 Transformer 就可以将任何 Observable<Object> 对象转换成 Observable<Boolean> 对象了。当然在 Transformer 里你也可以返回一个全新的 Observable 对象。 RxPermissions 就是这样做的,那么回到项目中再来看 compose(ensure(permissions)); 那么 ensure(permissions); 一定是返回一个 Transformer 对象了。</p>    <p>3.ensure(permissions);方法的实现</p>    <p>我们来看看 ensure(permissions) 方法的实现:</p>    <pre>  <code class="language-java">public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {          //创建一个Transformer对象返回          return new Observable.Transformer<Object, Boolean>() {              // o表示当前Observable对象。              @Override              public Observable<Boolean> call(Observable<Object> o) {                  //request(o, permissions) 方法返回 Observable<Permission>对象                  return request(o, permissions)                          // 将Observable<Permission>转换为Observable<Boolean>                          // buffer操作符                          .buffer(permissions.length)                          // flatMap操作符                          .flatMap(new Func1<List<Permission>, Observable<Boolean>>() {                              @Override                              public Observable<Boolean> call(List<Permission> permissions) {                                  // 如果permissions为空那么直接返回Observable.empty();                                  if (permissions.isEmpty()) {                                      // Occurs during orientation change, when the subject receives onComplete.                                      // In that case we don't want to propagate that empty list to the                                      // subscriber, only the onComplete.                                      return Observable.empty();                                  }                                  // 遍历所有Permission,如果有一个未成功则返回false,全部成功返回true。                                  for (Permission p : permissions) {                                      if (!p.granted) {                                          return Observable.just(false);                                      }                                  }                                  return Observable.just(true);                              }                          });              }          };      }</code></pre>    <p>确实是返回一个 Observable.Transformer 对象,那么在 call() 方法里首先调用了 request(o, permissions) 方法,然后又进行了 buffer() 和 flatMap() 的处理,最终会返回 Observable.empty(); 、 Observable.just(false); 或 Observable.just(true); 对象。我们先来看看 request(o, permissions) 方法的实现:</p>    <p>4.request(o, permissions);方法的实现</p>    <pre>  <code class="language-java">private Observable<Permission> request(final Observable<?> trigger,                                             final String... permissions) {          //如果并没有请求的权限则抛出异常          if (permissions == null || permissions.length == 0) {              throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");          }          return oneOf(trigger, pending(permissions))                  .flatMap(new Func1<Object, Observable<Permission>>() {                      @Override                      public Observable<Permission> call(Object o) {                          return request_(permissions);                      }                  });      }</code></pre>    <p>首先对 permissions 做了判断,然后调用了 oneOf(trigger, pending(permissions)) 方法,并通过 flatMap() 操作符在 call() 方法中调用了 request_(permissions); 按照惯例我们应该去看 oneOf() 方法的实现了。但是 oneOf() 方法里其实并没有什么实际意义,看了项目的 commit log 我觉得应该是历史遗留问题,作者可能是想处理一些相同重复的权限请求,但是并没有实现。所以其实这个方法完全可以这样代替;</p>    <pre>  <code class="language-java">private Observable<Permission> request(final Observable<?> trigger,                                             final String... permissions) {         return request_(permissions);      }</code></pre>    <p>直接调用 request_(permissions); 即可。我测试中并没有发现问题。目前我还没有联系到作者询问这个方法的实现目的,稍后可能会提一个 issue ,如果有结果会在文章中更新。所以这里大家就完全可以理解成直接调用了 request_(permissions); 方法:</p>    <p>5.request_(permissions);方法的实现</p>    <pre>  <code class="language-java">@TargetApi(Build.VERSION_CODES.M)      private Observable<Permission> request_(final String... permissions) {            //创建出一个存放Observable<Permission>的list          List<Observable<Permission>> list = new ArrayList<>(permissions.length);          //存放还为请求权限的list          List<String> unrequestedPermissions = new ArrayList<>();            // 在请求多个权限的时候,我们为每一个请求的权限都创建一个observable对象,在最后          // 这些observable会被合并成一个response。          for (String permission : permissions) {              log("Requesting permission " + permission);              //如果是已经获得的权限,或者Android版本在6.0之前则直接添加一个              // Observable.just(new Permission(permission, true))对象.              if (isGranted(permission)) {                  // Already granted, or not Android M                  // Return a granted Permission object.                  list.add(Observable.just(new Permission(permission, true)));                  continue;              }              // 如果是已经拒绝的权限则添加              // Observable.just(new Permission(permission, false))对象.              if (isRevoked(permission)) {                  // Revoked by a policy, return a denied Permission object.                  list.add(Observable.just(new Permission(permission, false)));                  continue;              }                PublishSubject<Permission> subject = mSubjects.get(permission);              // 如果mSubjects 不存在当前 permission,则添加到unrequestedPermissions中              // 并且创建PublishSubject对象并添加到mSubjects中。              if (subject == null) {                  unrequestedPermissions.add(permission);                  subject = PublishSubject.create();                  mSubjects.put(permission, subject);              }              //并且添加到list中              list.add(subject);          }            //如果有未请求的权限          if (!unrequestedPermissions.isEmpty()) {              startShadowActivity(unrequestedPermissions                      .toArray(new String[unrequestedPermissions.size()]));          }          return Observable.concat(Observable.from(list));      }        void startShadowActivity(String[] permissions) {          log("startShadowActivity " + TextUtils.join(", ", permissions));          Intent intent = new Intent(mCtx, ShadowActivity.class);          intent.putExtra("permissions", permissions);          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);          mCtx.startActivity(intent);      }</code></pre>    <p>代码如上,注释非常清晰,整体上就是将已经允许的权限和已经拒绝过的权限添加到 list 中,并且将还未请求的权限分别添加到 mSubjects 与 list 中。然后调用 startShadowActivity(); 方法。最后通过 Observable.concat(Observable.from(list)); 返回。这里主要包含 PublishSubject 与 concat() 操作符的知识:</p>    <p>6.PublishSubject对象</p>    <p>从 PublishSubject 的 文档 中,可以看出是继承自 Subject , Subject 是既可以充当 Observer 又能充当 Observable 的。从文档中的 Example 中可以看到订阅 PublishSubject 的 observer 只会接收到订阅之后 PublishSubject 发送的数据,但是本项目中并没有体现出此特性,主要是利用 PublishSubject 中的 onNext() 和 onCompleted() 方法,这里大致了解这么多,下面是 Example 的代码:</p>    <pre>  <code class="language-java">PublishSubject<Object> subject = PublishSubject.create();    // observer1 will receive all onNext and onCompleted events    subject.subscribe(observer1);    subject.onNext("one");    subject.onNext("two");    // observer2 will only receive "three" and onCompleted    subject.subscribe(observer2);    subject.onNext("three");    subject.onCompleted();</code></pre>    <p>7.concat()操作符</p>    <p>Concat 操作符将多个 Observable 结合成一个 Observable 并发射数据,并且严格按照先后顺序发射数据,前一个 Observable 的数据没有发射完,是不能发射后面 Observable 的数据的。所以在本项目中 Concat() 是为了保证请求的权限按顺序返回。接下来我们看看 ShadowActivity 的实现:</p>    <p>8.ShadowActivity的实现</p>    <pre>  <code class="language-java">@TargetApi(Build.VERSION_CODES.M)  public class ShadowActivity extends Activity {        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          if (savedInstanceState == null) {              handleIntent(getIntent());          }      }        @Override      protected void onNewIntent(Intent intent) {          handleIntent(intent);      }        private void handleIntent(Intent intent) {          String[] permissions = intent.getStringArrayExtra("permissions");          requestPermissions(permissions, 42);      }        @Override      public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {          RxPermissions.getInstance(this).onRequestPermissionsResult(requestCode, permissions, grantResults);          finish();      }  }</code></pre>    <p>很简单其实就是按照系统提供给我们的方法进行权限请求,最后回调 RxPermissions 的 onRequestPermissionsResult() 方法:</p>    <p>9.onRequestPermissionsResult()方法的实现</p>    <pre>  <code class="language-java">void onRequestPermissionsResult(int requestCode,                                      String permissions[], int[] grantResults) {          for (int i = 0, size = permissions.length; i < size; i++) {              log("onRequestPermissionsResult  " + permissions[i]);              // 取出对应的PublishSubject对象              PublishSubject<Permission> subject = mSubjects.get(permissions[i]);              if (subject == null) {                  // No subject found                  throw new IllegalStateException("RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");              }              //从mSubjects移除              mSubjects.remove(permissions[i]);              //获取结果              boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;              //调用onNext()方法发送结果              subject.onNext(new Permission(permissions[i], granted));              //调用onCompleted()方法。              subject.onCompleted();          }      }</code></pre>    <p>简单的来说就是拿到结果并发送结果。所以就又回到了最初的 ensure(permissions); 方法中的 request(o, permissions) 之后,代码如下:</p>    <pre>  <code class="language-java">request(o, permissions)          // 将Observable<Permission>转换为Observable<Boolean>          // buffer操作符          .buffer(permissions.length)          // flatMap操作符          .flatMap(new Func1<List<Permission>, Observable<Boolean>>() {              @Override              public Observable<Boolean> call(List<Permission> permissions) {                  // 如果permissions为空那么直接返回Observable.empty();                  if (permissions.isEmpty()) {                      // Occurs during orientation change, when the subject receives onComplete.                      // In that case we don't want to propagate that empty list to the                      // subscriber, only the onComplete.                      return Observable.empty();                  }                  // 遍历所有Permission,如果有一个未成功则返回false,全部成功返回true。                  for (Permission p : permissions) {                      if (!p.granted) {                          return Observable.just(false);                      }                  }                  return Observable.just(true);              }          });</code></pre>    <p>所以这里会不断的发送 Observable<Permission> 对象,请求了几个权限就会发送几次,但是这里用了一个 buffer() 操作符,关于 buffer() 操作符:</p>    <p>10.buffer()操作符</p>    <p>buffer 英文是缓冲区的意思。所以Buffer操作符所要做的事情就是将数据按照规定的大小做一下缓存,然后将缓存的数据作为一个集合发射出去。详细可以看 这里 ,所以在本项目中就是讲这些 Observable<Permission> 转换成 Observable<List<Permission>> 对象,紧接着又使用了 flatMap() 操作符然后返回了我们最终的结果。以上就是整个的调用流程了,如果有不清楚的建议可以多多的调试 RxPermission 的代码以及查阅各种资料帮助理解。</p>    <p>11. requestEach()、ensureEach()、ensure()的实现</p>    <p>以上我们分析了 request() 方法的实现,看似好像还剩下上面三个方法没有分析。其实仔细看的同学应该已经看明白了。上面三个方法其实都是差不多的。如果你看懂了 request() 方法的实现,那么这三个方法你一定能看懂,有兴趣的同学可以自行去源码里研究。</p>    <h3>4.个人评价</h3>    <p>其实 Android 6.0 的权限处理我自己在项目中都没有使用过,因为拿目前国内市场来说,首先 Android 6.0 的手机占有量非常少。再者我们可以使用很简单的方法将 targetSdkVersion 设置为 22 来兼容 6.0 的权限处理。所以目前项目中应该很少需要使用 RxPermissions 这个项目。但是这个项目作为 RxJava 的学习资料是相当的好。从中我们可以学到大量的 RxJava 相关的使用知识。如果你现在在学习 RxJava ,强烈推荐这个项目。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/c8a30200e6b2</p>    <p> </p>