RxPermission是一个Android处理权限的工具类,可以方便的和RxJava结合发起权限请求。这个库和其他权限请求工具的区别在于,可以不用处理的权限请求的结果回调onRequestPermissionsResult()方法,我们来通过源码解析来看下是如何实现的。
1、可以调用RxPermissions的request()方法,该方法返回一个Observable对象,代表请求权限的结果。改方法可以同时请求多个权限,只有所有权限都允许后表示成功,如果有一个权限被禁止,则回调结果为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方便的结合在一起。