RxPermission原始碼解析

大師兄要瀟灑發表於2018-11-04

RxPermission原始碼解析

RxPermission是一個Android處理許可權的工具類,可以方便的和RxJava結合發起許可權請求。這個庫和其他許可權請求工具的區別在於,可以不用處理的許可權請求的結果回撥onRequestPermissionsResult()方法,我們來通過原始碼解析來看下是如何實現的。

使用方式:

1、可以呼叫RxPermissions的request()方法,該方法返回一個Observable物件,代表請求許可權的結果。該方法可以同時請求多個許可權,當所有許可權都被允許後結果為true;如果有一個許可權被禁止,則回撥結果為false。

    RxPermissions rxPermissions = new RxPermissions(this);
    rxPermissions.request(permission.WRITE_EXTERNAL_STORAGE, permission.CAMERA)
        .subscribe(new Consumer<Boolean>() {
            @Override
            public void accept(Boolean aBoolean) throws Exception {
                if(aBoolean){
                    //permission granted
                }
            }
        });
複製程式碼

如果要獲取請求的每個許可權的結果,可以使用requestEach()方法。該方法的回撥會被呼叫多次,回撥的Permission表示每個許可權請求的結果。

    rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE, permission.CAMERA)
        .subscribe(new Consumer<Permission>() {
            @Override
            public void accept(Permission permission) throws Exception {
    			//check permission
            }
        });
複製程式碼

2、一般開發中,通常是點選某個按鈕後才請求許可權,如果使用了RxBinding庫處理UI事件,可以很方便的和RxPermissions一起進行許可權請求。

    RxView.clicks(findViewById(R.id.enableCamera))
        // Ask for permissions when button is clicked
        .compose(rxPermissions.ensureEach(permission.CAMERA))
        .subscribe(new Consumer<Permission>() {
            @Override
            public void accept(Permission permission) {
                Log.i(TAG, "Permission result " + permission);
                if (permission.granted) {
    
                } 
            });
複製程式碼

RxPermissions提供ensure(),ensureEach()和ensureEachCombined()方法,這些方法會返回一個ObservableTransformer<T, Permission>物件,結合RxJava中的compose操作符,可以將View事件轉換為一個許可權請求。

ensure()方法類似request()方法,結果回撥的引數為Boolean型別,請求的全部許可權都成功時為true,否則為false。

ensureEach()方法類似requestEach()方法,請求多個許可權時,結果回撥會被呼叫多次,可以獲取到每個許可權的請求結果。

ensureEachCombined()方法會結果會回撥一次,回撥的引數為Permission型別,這個permission引數和requestEach()的不太一樣,在這個permission物件中會包含所有請求的名字,並且所有許可權都成功的話,permission的granted屬性會為true,否則為false。

原始碼解析

從RxPermissions的構造方法開始

    public RxPermissions(@NonNull final FragmentActivity activity) {
        mRxPermissionsFragment = getLazySingleton(activity.getSupportFragmentManager());
    }
    
    public RxPermissions(@NonNull final Fragment fragment) {
        mRxPermissionsFragment = getLazySingleton(fragment.getChildFragmentManager());
    }
複製程式碼

RxPermissions針對Activity和Fragment提供了兩個構造方法,最終都是獲取FragmentManager再呼叫getLazySingleton()方法。getLazeSingleton方法的原始碼如下:

    @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;
            }
        };
    }
複製程式碼

這個方法返回了一個Lazy物件,在lazy的物件的get()方法中建立了一個fragment物件並儲存下來。Lazy是一個實現懶載入的工具類,在第一次呼叫get()方法時,會執行建立操作,並且將建立的物件儲存下來。

    @FunctionalInterface
    public interface Lazy<V> {
        V get();
    }
複製程式碼

建立RxPermissions物件後,便可以進行許可權請求,先看RxPermissions的request()方法

    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensure(permissions));
    }
複製程式碼

TRIGGER是一個Object物件,用於開始請求,並且相容RxBinding相關的操作。

     static final Object TRIGGER = new Object();
複製程式碼

request()方法內部實際上是呼叫了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()) {
                                return Observable.empty();
                            }
                            for (Permission p : permissions) {
                                if (!p.granted) {
                                    return Observable.just(false);
                                }
                            }
                            return Observable.just(true);
                        }
                    });
            }
        };
    }
複製程式碼

ensure()方法中建立了個一個RxJava中的ObservableTransformer物件,將一個泛型T型別的Observable裝換成一個ObservableSource物件,ObservableSource是Observable的父型別,這個Boolean型別代表請求許可權的結果。

在ObservableTransformer的apply()方法中,呼叫了request(final Observable<?> trigger, final String... permissions)方法,然後使用buffer操作符快取所有的請求結果,在flatMap操作符中將結果轉換為Boolean型別,如果所有的許可權都授權成功則返回true,否則返回false。

在request(final Observable<?> trigger, final String... permissions)方法中有呼叫了requestImplementation()方法

    private Observable<Permission> requestImplementation(final String... permissions) {
        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        List<String> unrequestedPermissions = new ArrayList<>();
        
        for (String permission : permissions) {
            mRxPermissionsFragment.get().log("Requesting permission " + permission);
            if (isGranted(permission)) {
                list.add(Observable.just(new Permission(permission, true, false)));
                continue;
            }
            if (isRevoked(permission)) {
                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));
    }
複製程式碼

在這個方法中,會建立兩個列表,list為List<Observable>型別,包含每個許可權的請求;unrequestedPermissions為List型別,表示需要發起請求的許可權。

這裡首先會遍歷需要請求的所有許可權,如果已經該許可權已經被授予,則建立一個granted欄位為true的Permission物件加入到list中;如果許可權被撤回,則建立一個granted欄位為false的Permission物件加入list中。如果該許可權沒有被授予也沒有被撤回,就去請求許可權。

具體的方式是呼叫RxPermissionsFragment物件的getSubjectByPermission()方法獲取一個PublishSubject物件,Subject是一種既可以作為Observable也可以作為Observer的物件,整合自Observable類,並實現了Observer介面。如果獲取到subject物件,表示該許可權正在被請求;如果返回null,表示該許可權未被請求,會建立一個新的PublishSubject物件,並呼叫setSubjectForPermission方法儲存下來,同時將該許可權儲存在unrequestedPermissions列表中。

如果unrequestedPermissions不為空,表示存在需要請求的許可權,會呼叫requestPermissionsFromFragment()去請求許可權。

最後,使用fromIterable和concat操作符將結果轉換為Observable型別。

接下來看requestPermissionsFromFragment()方法

    void requestPermissionsFromFragment(String[] permissions) {
        mRxPermissionsFragment.get().log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
        mRxPermissionsFragment.get().requestPermissions(permissions);
    }
複製程式碼

列印日誌後呼叫的RxPermissionsFragemnt的requestPermissions方法。而RxPermissionsFragemnt的requestPermissions方法直接發起了許可權請求。在RxPermissionsFragemnt的void onRequestPermissionsResult() 回撥中處理請求的結果。

    void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
        for (int i = 0, size = permissions.length; i < size; i++) {
            PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
            if (subject == null) {
                // No subject found
                return;
            }
            mSubjects.remove(permissions[i]);
            boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
            subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
            subject.onComplete();
        }
    }
複製程式碼

在回撥中會找到每個許可權對應的subject物件,建立一個Permission物件儲存許可權的結果,通過subject將結果傳送出去。

再回到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()) {
                                return Observable.empty();
                            }
                            for (Permission p : permissions) {
                                if (!p.granted) {
                                    return Observable.just(false);
                                }
                            }
                            return Observable.just(true);
                        }
                    });
            }
        };
    }
複製程式碼

如上所述,ensure方法會將許可權的請求結果轉換為Boolean型別。

同樣的,與request()對應的requestEach()方法在內部會呼叫ensureEach()方法, 我們來看下ensureEach()的實現

    public <T> ObservableTransformer<T, Permission> ensureEach(final String... permissions) {
        return new ObservableTransformer<T, Permission>() {
            @Override
            public ObservableSource<Permission> apply(Observable<T> o) {
                return request(o, permissions);
            }
        };
    }
複製程式碼

ensureEach()同樣是建立了一個ObservableTransformer物件,與ensure()不同的是將Observable轉換為ObservableSource物件。在下游可以接收到每個許可權的結果,並做相應的處理。

除了ensure()和ensureEach()方法外,RxPermissions還提供了一個ensureEachCombined()方法,實現如下

    public <T> ObservableTransformer<T, Permission> ensureEachCombined(final String... permissions) {
        return new ObservableTransformer<T, Permission>() {
            @Override
            public ObservableSource<Permission> apply(Observable<T> o) {
                return request(o, permissions)
                    .buffer(permissions.length)
                    .flatMap(new Function<List<Permission>, ObservableSource<Permission>>() {
                        @Override
                        public ObservableSource<Permission> apply(List<Permission> permissions) {
                            if (permissions.isEmpty()) {
                                return Observable.empty();
                            }
                            return Observable.just(new Permission(permissions));
                        }
                    });
            }
        };
    }
複製程式碼

該方法與ensure方法類似,同樣會對請求的結果進行處理,只是是將所有的結果轉換為一個Permission物件,而不是一個Boolean值。Permission類有一個Permission(List permissions)建構函式,在一個Permission物件中存貯多個許可權的結果

    public Permission(List<Permission> permissions) {
        name = combineName(permissions);
        granted = combineGranted(permissions);
        shouldShowRequestPermissionRationale = combineShouldShowRequestPermissionRationale(permissions);
    }
複製程式碼

combineName()方法會多個許可權的名稱通過“,”連線成一個字串;combineGranted()方法當所有許可權都授予時會返回ture,否則返回false;combineShouldShowRequestPermissionRationale()方法則當任意一個許可權的shouldShowRequestPermissionRationale為ture時返回ture,否則返回false。下面是combineName()的原始碼

    private String combineName(List<Permission> permissions) {
        return Observable.fromIterable(permissions)
            .map(new Function<Permission, String>() {
                @Override
                public String apply(Permission permission) throws Exception {
                    return permission.name;
                }
            }).collectInto(new StringBuilder(), new BiConsumer<StringBuilder, String>() {
            @Override
            public void accept(StringBuilder s, String s2) throws Exception {
                if (s.length() == 0) {
                    s.append(s2);
                } else {
                    s.append(", ").append(s2);
                }
            }
        }).blockingGet().toString();
    }
複製程式碼

總結

RxPermissions通過在Activity新增一個fragment來請求許可權和處理許可權的結果,避免單獨再處理onRequestPermissionsResult回撥。並提供request和ensure等一系列方法,可以和RxJava,RxBinding方便的結合在一起。

相關文章