源碼剖析:RxPermissions 如何實(shí)現(xiàn)監(jiān)聽權(quán)限的變化

RxPermissions

正常情況下,是通過ContextCompat.checkSelfPermission檢查是否有權(quán)限,通過ActivityCompat.requestPermissions來獲取授權(quán),在onRequestPermissionsResult回調(diào)獲取授權(quán)結(jié)果,必須在一個(gè)Activity實(shí)現(xiàn)兩處代碼才可以完成整個(gè)授權(quán),非常的麻煩。

開源庫(kù) RxPermission 通過RxJava很好地封裝了一套方案,大大簡(jiǎn)化了權(quán)限申請(qǐng),我們現(xiàn)在剖析RxPermission的源碼,看看他是如何實(shí)現(xiàn)的。

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
        }
    });
代碼結(jié)構(gòu)

RxPermissions的代碼很少,所有代碼都在以下目錄中,只有三個(gè)類,全部代碼量只有五百多行。

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

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

可以看到非常有趣的事情,一個(gè)權(quán)限申請(qǐng)庫(kù)為何會(huì)有一個(gè)Fragment類,有何作用呢?

構(gòu)造方法源碼

我們從RxPermissions的構(gòu)造函數(shù)開始,提供了兩個(gè)構(gòu)造函數(shù),可以傳入FragmentActivityFragment,他們都是用于創(chuàng)建RxPermissionsFragment,我們都知道授權(quán)后需要在 Activity 或者 Fragment 的onRequestPermissionsResult的回調(diào)方法才能知道是否授權(quán)成功,所以猜測(cè)RxPermissionsFragment是用于獲取響應(yīng)授權(quán)信息。

    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方法,我們看到一個(gè)非常有趣的寫法,這里使用Lazy封裝了一種懶加載的方式,在構(gòu)造方法就已經(jīng)傳入的相關(guān)的創(chuàng)建Fragment的參數(shù),但是并沒有馬上創(chuàng)建,等真正需要使用時(shí)候調(diào)用mRxPermissionsFragment .get()才創(chuàng)建Fragment實(shí)體。

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

Observable.just() :just操作符可以將某個(gè)對(duì)象轉(zhuǎn)化為Observable對(duì)象,是RxJava中非常快捷的創(chuàng)建Observable對(duì)象的方法。

compose():該操作符是針對(duì)Observable自身的變換,通過我們自己定義的Transformer對(duì)象可以將對(duì)Observable對(duì)象變換的操作封裝起來,比如可以把,甚至返回一個(gè)全新的Observable。

看到請(qǐng)求權(quán)限的入口是request(),這里用到了Observable.just(TRIGGER),然后調(diào)用了compose()操作符,這里這里可以看到,實(shí)際上相關(guān)的權(quán)限申請(qǐng)?zhí)幚矸庋b在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:這個(gè)是RxJava的一個(gè)操作符,字面意思就是緩沖,其實(shí)就是緩存多個(gè)Observable響應(yīng),等多個(gè)Observable返回結(jié)果后才一起進(jìn)行下一步的操作,這里就是把多個(gè)權(quán)限申請(qǐng)的結(jié)果合并為一個(gè)結(jié)果返回。

這里的關(guān)鍵的代碼在request(o, permissions).flatMap(new Function<List<Permission>,...先看后者,后者是對(duì)前者Observable列表響應(yīng)的權(quán)限進(jìn)行轉(zhuǎn)換,由于是可以同時(shí)進(jìn)行多個(gè)權(quán)限的請(qǐng)求,如果多個(gè)權(quán)限申請(qǐng)中某個(gè)權(quán)限沒有通過都會(huì)返回false;那么我們進(jìn)一步看前者的代碼。

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);
                    }
                });
    }

這里的代碼首先對(duì)傳入的permissions權(quán)限列表進(jìn)行判斷,不允許傳入空的數(shù)據(jù),否則就會(huì)拋出異常。然后就是return oneOf(trigger, pending(permissions))...這部分代碼,我無法理解這里代碼的意義,在我實(shí)際測(cè)試,直接return requestImplementation(permissions);也是可以實(shí)現(xiàn)同樣的功能,所以這里的代碼就不再展開,直接下一步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:這個(gè)類是RxJava重要的類之一,我們有必要詳細(xì)了解一下,這里就簡(jiǎn)單描述,PublishSubject繼承于Subject,與普通的Subject不同,在訂閱時(shí)并不立即觸發(fā)訂閱事件,而是允許我們?cè)谌我鈺r(shí)刻手動(dòng)調(diào)用onNext,onError(),onCompleted來觸發(fā)事件。比如可用在Service下載多個(gè)文件,使用PublishSubject來監(jiān)聽具體的情況,然后響應(yīng)給Activity(相當(dāng)于EventBus的功能)。

這個(gè)方法的代碼比較多,但是也是比較重要的一部分,從代碼的注釋和方法命名,我們就可以理解這段代碼的意思,其實(shí)就是對(duì)傳入的權(quán)限進(jìn)行判斷,isGranted(permission)判斷APP是否已經(jīng)獲得該權(quán)限,isRevoked(permission)用于判斷APP是否在AndroidManifest.xml申請(qǐng)了權(quán)限,如果沒有獲得權(quán)限就在RxPermissionsFragment創(chuàng)建一個(gè)一一對(duì)應(yīng)的PublishSubject,用于監(jiān)聽權(quán)限的響應(yīng)情況,方法的最后就是requestPermissionsFromFragment,真正的發(fā)起權(quán)限申請(qǐng)的地方就是這里了,那么接下來我們就開始分析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();
        }
    }

這里的代碼很簡(jiǎn)單,其實(shí)就是發(fā)起權(quán)限申請(qǐng)后,獲取響應(yīng)的情況,grantResults獲取申請(qǐng)是否已經(jīng)申請(qǐng)成功,shouldShowRequestPermissionRationale是用來獲取用戶是否勾選了禁止后不再詢問,通過PublishSubject響應(yīng)結(jié)果。到了這一步,我們可以重新回到前面的ensure()段落重新看待這部分的代碼就可以理解整個(gè)過程。

總結(jié)

RxPermissions的代碼量不多,由于無法做到非入侵式監(jiān)聽Activity的onRequestPermissionsResult,所以非常奇妙地創(chuàng)建一個(gè)Fragment來實(shí)現(xiàn)監(jiān)聽的功能,設(shè)計(jì)得非常優(yōu)雅。也大量使用了RxJava操作符,簡(jiǎn)化了各種流程的轉(zhuǎn)接問題,這個(gè)庫(kù)也是學(xué)習(xí)RxJava非常重要的素材,非常值得研究。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容