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方便的結合在一起。