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的构造函数开始,提供了两个构造函数,可以传入FragmentActivity
或Fragment
,他们都是用于创建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非常重要的素材,非常值得研究。