美文网首页Android收藏集Android开发Android开发经验谈
源码剖析:RxPermissions 如何实现监听权限的变化

源码剖析:RxPermissions 如何实现监听权限的变化

作者: ImWiki | 来源:发表于2019-07-24 02:10 被阅读20次

    RxPermissions

    正常情况下,是通过ContextCompat.checkSelfPermission检查是否有权限,通过ActivityCompat.requestPermissions来获取授权,在onRequestPermissionsResult回调获取授权结果,必须在一个Activity实现两处代码才可以完成整个授权,非常的麻烦。

    开源库 RxPermission 通过RxJava很好地封装了一套方案,大大简化了权限申请,我们现在剖析RxPermission的源码,看看他是如何实现的。

    RxPermissions rxPermissions = new RxPermissions(this);
    rxPermissions.request(Manifest.permission.CAMERA).subscribe(granted -> {
            if (granted) { // Always true pre-M
               // I can control the camera now
            } else {
               // Oups permission denied
            }
        });
    
    代码结构

    RxPermissions的代码很少,所有代码都在以下目录中,只有三个类,全部代码量只有五百多行。

    lib/src/main/java/com/tbruyelle/rxpermissions2

    └── rxpermissions2
        ├── Permission.java
        ├── RxPermissions.java
        └── RxPermissionsFragment.java
    

    可以看到非常有趣的事情,一个权限申请库为何会有一个Fragment类,有何作用呢?

    构造方法源码

    我们从RxPermissions的构造函数开始,提供了两个构造函数,可以传入FragmentActivityFragment,他们都是用于创建RxPermissionsFragment,我们都知道授权后需要在 Activity 或者 Fragment 的onRequestPermissionsResult的回调方法才能知道是否授权成功,所以猜测RxPermissionsFragment是用于获取响应授权信息。

        public RxPermissions(@NonNull final FragmentActivity activity) {
            mRxPermissionsFragment = getLazySingleton(activity.getSupportFragmentManager());
        }
        public RxPermissions(@NonNull final Fragment fragment) {
            mRxPermissionsFragment = getLazySingleton(fragment.getChildFragmentManager());
        }
        @NonNull
        private Lazy<RxPermissionsFragment> getLazySingleton(@NonNull final FragmentManager fragmentManager) {
            return new Lazy<RxPermissionsFragment>() {
                private RxPermissionsFragment rxPermissionsFragment;
                @Override
                public synchronized RxPermissionsFragment get() {
                    if (rxPermissionsFragment == null) {
                        rxPermissionsFragment = getRxPermissionsFragment(fragmentManager);
                    }
                    return rxPermissionsFragment;
                }
            };
        }
    

    懒加载:从上面的getLazySingleton方法,我们看到一个非常有趣的写法,这里使用Lazy封装了一种懒加载的方式,在构造方法就已经传入的相关的创建Fragment的参数,但是并没有马上创建,等真正需要使用时候调用mRxPermissionsFragment .get()才创建Fragment实体。

    RxPermissions.request() 入口分析
        public Observable<Boolean> request(final String... permissions) {
            return Observable.just(TRIGGER).compose(ensure(permissions));
        }
    

    Observable.just() :just操作符可以将某个对象转化为Observable对象,是RxJava中非常快捷的创建Observable对象的方法。

    compose():该操作符是针对Observable自身的变换,通过我们自己定义的Transformer对象可以将对Observable对象变换的操作封装起来,比如可以把,甚至返回一个全新的Observable。

    看到请求权限的入口是request(),这里用到了Observable.just(TRIGGER),然后调用了compose()操作符,这里这里可以看到,实际上相关的权限申请处理封装在ensure()方法中。

    ensure()
        public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
            return new ObservableTransformer<T, Boolean>() {
                @Override
                public ObservableSource<Boolean> apply(Observable<T> o) {
                    return request(o, permissions)
                            // Transform Observable<Permission> to Observable<Boolean>
                            .buffer(permissions.length)
                            .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {
                                @Override
                                public ObservableSource<Boolean> apply(List<Permission> permissions) {
                                    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();
                                    }
                                    // Return true if all permissions are granted.
                                    for (Permission p : permissions) {
                                        if (!p.granted) {
                                            return Observable.just(false);
                                        }
                                    }
                                    return Observable.just(true);
                                }
                            });
                }
            };
        }
    

    buffer:这个是RxJava的一个操作符,字面意思就是缓冲,其实就是缓存多个Observable响应,等多个Observable返回结果后才一起进行下一步的操作,这里就是把多个权限申请的结果合并为一个结果返回。

    这里的关键的代码在request(o, permissions).flatMap(new Function<List<Permission>,...先看后者,后者是对前者Observable列表响应的权限进行转换,由于是可以同时进行多个权限的请求,如果多个权限申请中某个权限没有通过都会返回false;那么我们进一步看前者的代码。

    request(o, permissions)
        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 Function<Object, Observable<Permission>>() {
                        @Override
                        public Observable<Permission> apply(Object o) {
                            return requestImplementation(permissions);
                        }
                    });
        }
    

    这里的代码首先对传入的permissions权限列表进行判断,不允许传入空的数据,否则就会抛出异常。然后就是return oneOf(trigger, pending(permissions))...这部分代码,我无法理解这里代码的意义,在我实际测试,直接return requestImplementation(permissions);也是可以实现同样的功能,所以这里的代码就不再展开,直接下一步requestImplementation分析。

    requestImplementation(permissions)
    @TargetApi(Build.VERSION_CODES.M)
        private Observable<Permission> requestImplementation(final String... permissions) {
            List<Observable<Permission>> list = new ArrayList<>(permissions.length);
            List<String> unrequestedPermissions = new ArrayList<>();
    
            // In case of multiple permissions, we create an Observable for each of them.
            // At the end, the observables are combined to have a unique response.
            for (String permission : permissions) {
                mRxPermissionsFragment.get().log("Requesting permission " + permission);
                if (isGranted(permission)) {
                    // Already granted, or not Android M
                    // Return a granted Permission object.
                    list.add(Observable.just(new Permission(permission, true, false)));
                    continue;
                }
    
                if (isRevoked(permission)) {
                    // Revoked by a policy, return a denied Permission object.
                    list.add(Observable.just(new Permission(permission, false, false)));
                    continue;
                }
    
                PublishSubject<Permission> subject = mRxPermissionsFragment.get().getSubjectByPermission(permission);
                // Create a new subject if not exists
                if (subject == null) {
                    unrequestedPermissions.add(permission);
                    subject = PublishSubject.create();
                    mRxPermissionsFragment.get().setSubjectForPermission(permission, subject);
                }
    
                list.add(subject);
            }
    
            if (!unrequestedPermissions.isEmpty()) {
                String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
                requestPermissionsFromFragment(unrequestedPermissionsArray);
            }
            return Observable.concat(Observable.fromIterable(list));
        }
        @TargetApi(Build.VERSION_CODES.M)
        void requestPermissionsFromFragment(String[] permissions) {
            mRxPermissionsFragment.get().log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
            mRxPermissionsFragment.get().requestPermissions(permissions);
        }
    

    PublishSubject:这个类是RxJava重要的类之一,我们有必要详细了解一下,这里就简单描述,PublishSubject继承于Subject,与普通的Subject不同,在订阅时并不立即触发订阅事件,而是允许我们在任意时刻手动调用onNext,onError(),onCompleted来触发事件。比如可用在Service下载多个文件,使用PublishSubject来监听具体的情况,然后响应给Activity(相当于EventBus的功能)。

    这个方法的代码比较多,但是也是比较重要的一部分,从代码的注释和方法命名,我们就可以理解这段代码的意思,其实就是对传入的权限进行判断,isGranted(permission)判断APP是否已经获得该权限,isRevoked(permission)用于判断APP是否在AndroidManifest.xml申请了权限,如果没有获得权限就在RxPermissionsFragment创建一个一一对应的PublishSubject,用于监听权限的响应情况,方法的最后就是requestPermissionsFromFragment,真正的发起权限申请的地方就是这里了,那么接下来我们就开始分析RxPermissionsFragment的onRequestPermissionsResult方法。

    onRequestPermissionsResult
        @Override
        @TargetApi(Build.VERSION_CODES.M)
        public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            if (requestCode != PERMISSIONS_REQUEST_CODE) return;
            boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
            for (int i = 0; i < permissions.length; i++) {
                shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);
            }
            onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);
        }
    
        void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
            for (int i = 0, size = permissions.length; i < size; i++) {
                log("onRequestPermissionsResult  " + permissions[i]);
                // Find the corresponding subject
                PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
                if (subject == null) {
                    // No subject found
                    Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
                    return;
                }
                mSubjects.remove(permissions[i]);
                boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
                subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
                subject.onComplete();
            }
        }
    

    这里的代码很简单,其实就是发起权限申请后,获取响应的情况,grantResults获取申请是否已经申请成功,shouldShowRequestPermissionRationale是用来获取用户是否勾选了禁止后不再询问,通过PublishSubject响应结果。到了这一步,我们可以重新回到前面的ensure()段落重新看待这部分的代码就可以理解整个过程。

    总结

    RxPermissions的代码量不多,由于无法做到非入侵式监听Activity的onRequestPermissionsResult,所以非常奇妙地创建一个Fragment来实现监听的功能,设计得非常优雅。也大量使用了RxJava操作符,简化了各种流程的转接问题,这个库也是学习RxJava非常重要的素材,非常值得研究。

    相关文章

      网友评论

        本文标题:源码剖析:RxPermissions 如何实现监听权限的变化

        本文链接:https://www.haomeiwen.com/subject/rtqwlctx.html