RxPermissions
正常情況下,是通過ContextCompat.checkSelfPermission
檢查是否有權(quán)限辨宠,通過ActivityCompat.requestPermissions
來獲取授權(quán)预麸,在onRequestPermissionsResult
回調(diào)獲取授權(quán)結(jié)果盖溺,必須在一個Activity實現(xiàn)兩處代碼才可以完成整個授權(quán)砰苍,非常的麻煩莫鸭。
開源庫 RxPermission 通過RxJava很好地封裝了一套方案闹丐,大大簡化了權(quán)限申請,我們現(xiàn)在剖析RxPermission的源碼被因,看看他是如何實現(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的代碼很少衫仑,所有代碼都在以下目錄中,只有三個類堕花,全部代碼量只有五百多行文狱。
lib/src/main/java/com/tbruyelle/rxpermissions2
└── rxpermissions2
├── Permission.java
├── RxPermissions.java
└── RxPermissionsFragment.java
可以看到非常有趣的事情,一個權(quán)限申請庫為何會有一個Fragment類缘挽,有何作用呢瞄崇?
構(gòu)造方法源碼
我們從RxPermissions的構(gòu)造函數(shù)開始,提供了兩個構(gòu)造函數(shù)壕曼,可以傳入FragmentActivity
或Fragment
苏研,他們都是用于創(chuàng)建RxPermissionsFragment
,我們都知道授權(quán)后需要在 Activity 或者 Fragment 的onRequestPermissionsResult
的回調(diào)方法才能知道是否授權(quán)成功窝稿,所以猜測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
方法,我們看到一個非常有趣的寫法伴榔,這里使用Lazy
封裝了一種懶加載的方式纹蝴,在構(gòu)造方法就已經(jīng)傳入的相關(guān)的創(chuàng)建Fragment的參數(shù),但是并沒有馬上創(chuàng)建踪少,等真正需要使用時候調(diào)用mRxPermissionsFragment .get()
才創(chuàng)建Fragment實體塘安。
RxPermissions.request() 入口分析
public Observable<Boolean> request(final String... permissions) {
return Observable.just(TRIGGER).compose(ensure(permissions));
}
Observable.just() :just操作符可以將某個對象轉(zhuǎn)化為Observable對象,是RxJava中非吃荩快捷的創(chuàng)建Observable對象的方法兼犯。
compose():該操作符是針對Observable自身的變換,通過我們自己定義的Transformer對象可以將對Observable對象變換的操作封裝起來集漾,比如可以把切黔,甚至返回一個全新的Observable。
看到請求權(quán)限的入口是request()
具篇,這里用到了Observable.just(TRIGGER)
纬霞,然后調(diào)用了compose()
操作符,這里這里可以看到驱显,實際上相關(guān)的權(quán)限申請?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:這個是RxJava的一個操作符,字面意思就是緩沖埃疫,其實就是緩存多個Observable響應(yīng)伏恐,等多個Observable返回結(jié)果后才一起進行下一步的操作,這里就是把多個權(quán)限申請的結(jié)果合并為一個結(jié)果返回栓霜。
這里的關(guān)鍵的代碼在request(o, permissions)
和.flatMap(new Function<List<Permission>,...
先看后者翠桦,后者是對前者Observable列表
響應(yīng)的權(quán)限進行轉(zhuǎn)換,由于是可以同時進行多個權(quán)限的請求胳蛮,如果多個權(quán)限申請中某個權(quán)限沒有通過都會返回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權(quán)限列表進行判斷,不允許傳入空的數(shù)據(jù)闻鉴,否則就會拋出異常。然后就是return oneOf(trigger, pending(permissions))...
這部分代碼茂洒,我無法理解這里代碼的意義孟岛,在我實際測試,直接return requestImplementation(permissions);
也是可以實現(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:這個類是RxJava重要的類之一智哀,我們有必要詳細(xì)了解一下次询,這里就簡單描述,PublishSubject繼承于Subject瓷叫,與普通的Subject不同屯吊,在訂閱時并不立即觸發(fā)訂閱事件,而是允許我們在任意時刻手動調(diào)用onNext,onError(),onCompleted來觸發(fā)事件摹菠。比如可用在Service下載多個文件盒卸,使用PublishSubject來監(jiān)聽具體的情況,然后響應(yīng)給Activity(相當(dāng)于EventBus的功能)次氨。
這個方法的代碼比較多蔽介,但是也是比較重要的一部分,從代碼的注釋和方法命名煮寡,我們就可以理解這段代碼的意思虹蓄,其實就是對傳入的權(quán)限進行判斷,isGranted(permission)
判斷APP是否已經(jīng)獲得該權(quán)限幸撕,isRevoked(permission)
用于判斷APP是否在AndroidManifest.xml申請了權(quán)限薇组,如果沒有獲得權(quán)限就在RxPermissionsFragment創(chuàng)建一個一一對應(yīng)的PublishSubject,用于監(jiān)聽權(quán)限的響應(yīng)情況杈帐,方法的最后就是requestPermissionsFromFragment
体箕,真正的發(fā)起權(quán)限申請的地方就是這里了,那么接下來我們就開始分析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();
}
}
這里的代碼很簡單累铅,其實就是發(fā)起權(quán)限申請后,獲取響應(yīng)的情況站叼,grantResults
獲取申請是否已經(jīng)申請成功娃兽,shouldShowRequestPermissionRationale
是用來獲取用戶是否勾選了禁止后不再詢問
,通過PublishSubject響應(yīng)結(jié)果尽楔。到了這一步投储,我們可以重新回到前面的ensure()
段落重新看待這部分的代碼就可以理解整個過程第练。
總結(jié)
RxPermissions的代碼量不多,由于無法做到非入侵式監(jiān)聽Activity的onRequestPermissionsResult
玛荞,所以非常奇妙地創(chuàng)建一個Fragment來實現(xiàn)監(jiān)聽的功能娇掏,設(shè)計得非常優(yōu)雅。也大量使用了RxJava操作符勋眯,簡化了各種流程的轉(zhuǎn)接問題婴梧,這個庫也是學(xué)習(xí)RxJava非常重要的素材,非常值得研究客蹋。