由于在项目中用到了RxPermissions框架,所以想看看源码,顺便记录一下自己对该框架的分析过程。
下面是一篇讲有关Android权限基础知识的文章,有心的小伙伴可以参考。
使用RxPermissions(基于RxJava2)
- App module的build.gradle
dependencies {
…
compile ‘com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar’
compile ‘io.reactivex.rxjava2:rxjava:2.1.13’
…
}
首先需要在Activity里的onCreate方法里写入这段代码
rxPermissions=new RxPermissions(this);
接下来,我们看看上面这段话对应的源代码
public RxPermissions(@NonNull Activity activity) {mRxPermissionsFragment = getRxPermissionsFragment(activity);
}private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);boolean isNewInstance = rxPermissionsFragment == null;if (isNewInstance) {rxPermissionsFragment = new RxPermissionsFragment();FragmentManager fragmentManager = activity.getFragmentManager();fragmentManager.beginTransaction().add(rxPermissionsFragment, TAG).commitAllowingStateLoss();fragmentManager.executePendingTransactions();}return rxPermissionsFragment;
}private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
从这段源码中,我们可以看出首先创建了一个没有布局的隐形RxPermissionsFragment,最后其实是使用这个隐形碎片放的requestPermissions()&onRequestPermissionsResult()方法实现请求权限以及回调的过程,我看之前的fix46分支见下图
是使用一个Activity,而不是用一个Fragment,不明白作者在新的分支master用碎片有什么好处?
使用rxPermissions请求两个运行时权限,请看下面这段代码
private void checkPermission(){rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION).subscribe(granted -> {if (granted) {执行其他代码} else {Toast.makeText(TrainActivity.this,"You denied the permission",Toast.LENGTH_SHORT).show();}});}
在这里涉及到了大名鼎鼎的RxJava,其实这个框架也是采用了RxJava的设计思想,如果对RxJava一点也不了解的小伙伴,可以看看下面这个人写的RxJava入门教程,自认为堪称经典,尤其是使用两个水管代替Observable&Observer的妙喻,让我在没有间断的时间内通篇看完之后有一种酣畅淋漓、欲罢不能的赶脚。下面扔出这个教程的链接:给初学者的RxJava2.0教程
下面点击request方法进入到其内部看看如何实现的
public Observable<Boolean> request(final String... permissions) {return Observable.just(TRIGGER).compose(ensure(permissions));}
首先使用Observable.just(TRIGGER)获得一个Observable对象,然后再调用compose方法,网上有人说调用该方法是为了消除重复代码,但我感觉这种说法太官方,没有将这方法放置在这源码中进行解释,但我又没有明白作者之意?
后面发现compose方法需要传入的参数是ObservableTransformer< T, R >,目的是为了完成Observable< T > 的对象转换成 Observable< R > 对象。接下来我们看看ensure方法。
public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {return new ObservableTransformer<T, Boolean>() {@Overridepublic 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>>() {@Overridepublic ObservableSource<Boolean> apply(List<Permission> permissions) throws Exception {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);}});}};}
request(o, permissions)方法返回的对象是Observable< Permission >,然后调用buffer方法将所有请求的权限事件放到缓冲区当中一并发出,之后再调用flatMap方法将List< Permission > 变换成为 ObservableSource< Boolean > ,最后将结果传递给如下的代码。
.subscribe(granted -> {if (granted) {openWiFi();} else {Toast.makeText(TrainActivity.this,"You denied the permission",Toast.LENGTH_SHORT).show();}});
下面进入到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>>() {@Overridepublic Observable<Permission> apply(Object o) throws Exception {return requestImplementation(permissions);}});}
首先如果没有传入权限字符串,则抛出异常。当传入权限字符串时,则返回Observable< Permission >,有博客说oneof方法没有实际的意义,当时我在想为什么这样写,而不是写成Observable.just(TRIGGER)这样?作者的真实意图又是什么呢?自己认为该开源框架中最核心的方法就是requestImplementation(permissions),下面将对该方法进行详细分析。
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.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.getSubjectByPermission(permission);// Create a new subject if not existsif (subject == null) {unrequestedPermissions.add(permission);subject = PublishSubject.create();mRxPermissionsFragment.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集合中存放着请求的所有权限,不论是否申请,而在unrequestedPermissions集合中存放着未申请的全部权限,进入到for循环中,判断每一个permission的状态,其状态共有三种:已授权(Granted)、已拒绝(Revoked)、未申请(unrequested),首先判断是否授权,如果授权则将 new Permission对象里参数granted赋值为true,其次判断是否被拒绝,如果被拒绝则将 new Permission对象里参数granted赋值为false,最后将未申请的权限放到集合unrequestedPermissions,并且给每一个未申请的权限创建一个subject对象,该对象既继承Observable,又实现了Observer。分析该方法的过程中,突然想到一个问题:当用户一开始申请权限时都应该是未申请的权限,是不是没必要设计这isGranted(permission)和isRevoked(permission)两个方法,接下来继续看未申请的权限集合unrequestedPermissions,如果未申请权限的集合不为空,则将一个ArrayList集合转化成为一个Array数据,然后再执行requestPermissionsFromFragment方法,接下来再看看该方法。
void requestPermissionsFromFragment(String[] permissions) {mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));mRxPermissionsFragment.requestPermissions(permissions);}
继续跟踪看看requestPermissions()方法
@TargetApi(Build.VERSION_CODES.M)void requestPermissions(@NonNull String[] permissions) {requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);}
到此为止,就回到了最原始的请求方法requestPermissions()与回调请求权限结果的方法onRequestPermissionsResult()中来了,我们再来看看onRequestPermissionsResult()方法。
@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 subjectPublishSubject<Permission> subject = mSubjects.get(permissions[i]);if (subject == null) {// No subject foundLog.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();}}
如果请求码不等于权限请求码,则直接返回。接下来我们看看shouldShowRequestPermissionRationale()方法,该方法是AppCompact里面的。用于权限管理。
我们再来看看最后一个onRequestPermissionsResult()方法,首先是从mSubjects对象中获取到一个subject,在这里我认为subject应该看做是一个Observable(被观察者),然后再组装一个new Permission对象,然后通过onNext方法发射出去,最后再执行onComplete()方法。
RxPermissions源码分析过程就已经全部结束,中间还有很多的困惑等待解决,日后如果弄明白了,再加以补充。
近日在郭大婶的公众号中看到了他自己又新开源了一个库PermissionX,其核心的思想与RxPermissions颇有相通之处,是用kotlin语言写的,可借鉴学习。其地址如下我新开发了一个特别好用的开源库