項(xiàng)目地址:RxPermissions,本文分析版本: 4c4d4e1
1.簡(jiǎn)介
RxPermissions是基于RxJava開(kāi)發(fā)的用于幫助在Android 6.0中處理運(yùn)行時(shí)權(quán)限檢測(cè)的框架。在Android 6.0中,系統(tǒng)新增了部分權(quán)限的運(yùn)行時(shí)動(dòng)態(tài)獲取。而不再是在以前的版本中安裝的時(shí)候授予權(quán)限。
對(duì)于運(yùn)行時(shí)的權(quán)限獲取提示,國(guó)內(nèi)的Android工程師們應(yīng)該并不陌生,國(guó)內(nèi)的第三方ROM例如MIUI在很早前就做了類(lèi)似的功能。但是第三方ROM并不能提供給我們權(quán)限請(qǐng)求成功或失敗的接口,這就導(dǎo)致我們無(wú)法通過(guò)PackageManager提供的checkPermission()方法來(lái)準(zhǔn)確的獲取到我們是否獲得該權(quán)限。只能根據(jù)具體的權(quán)限來(lái)做相應(yīng)的處理。但是在Android 6.0中我們可以準(zhǔn)確的獲取我們的應(yīng)用是否獲取某個(gè)權(quán)限,具體的方法希望大家看這篇文章:Android 6.0 運(yùn)行時(shí)權(quán)限處理。大致方法是通過(guò)API 23中的Activity的requestPermissions(String[] permissions, int requestCode);方法請(qǐng)求權(quán)限,并在onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults)回調(diào)方法中處理請(qǐng)求結(jié)果。這個(gè)方法與onActivityResult()類(lèi)似。RxPermissions在這個(gè)基礎(chǔ)上做了封裝,使我們?cè)谔幚磉\(yùn)行時(shí)權(quán)限變得更加的簡(jiǎn)單。
另外關(guān)于RxJava如果你現(xiàn)在還沒(méi)了解過(guò)RxJava可以直接翻到文章最下面去查看我總結(jié)的一些RxJava相關(guān)的文章,不然并不推薦直接看這篇文章。下面我們就來(lái)具體看看RxPermissions的使用方法以及源碼分析。
2.使用方法
1.直接獲取權(quán)限(使用Retrolambda使代碼更加簡(jiǎn)潔,當(dāng)然并不是必須使用):
// 必須在初始化階段調(diào)用,例如onCreate()方法中
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(granted -> {
if (granted) { // 在android 6.0之前會(huì)默認(rèn)返回true
// 已經(jīng)獲取權(quán)限
} else {
// 未獲取權(quán)限
}
});
2.通過(guò)條件觸發(fā)獲取權(quán)限(結(jié)合RxBinding使用)
// 必須在初始化階段調(diào)用,例如onCreate()方法中
RxView.clicks(findViewById(R.id.enableCamera))
.compose(RxPermissions.getInstance(this).ensure(Manifest.permission.CAMERA))
.subscribe(granted -> {
// 當(dāng)R.id.enableCamera被點(diǎn)擊的時(shí)候觸發(fā)獲取權(quán)限
});
3.一次請(qǐng)求多個(gè)權(quán)限(有兩種方式)
如果同時(shí)請(qǐng)求多個(gè)權(quán)限,下面這種方式會(huì)合并請(qǐng)求結(jié)果,即所有權(quán)限請(qǐng)求成功會(huì)返回true,若有一個(gè)權(quán)限未成功則返回false。
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(granted -> {
if (granted) {
// 所有權(quán)限請(qǐng)求被同意
} else {
// 至少有一個(gè)權(quán)限沒(méi)同意
}
});
當(dāng)然你可以通過(guò)requestEach or ensureEach 來(lái)分別獲取每一個(gè)權(quán)限請(qǐng)求的結(jié)果。
RxPermissions.getInstance(this)
.requestEach(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(permission -> { // 會(huì)發(fā)送兩個(gè)Permission對(duì)象
if (permission.granted) {
// `permission.name` is granted !
}
});
注意:由于在請(qǐng)求權(quán)限的過(guò)程中app有可能會(huì)被重啟,所以權(quán)限請(qǐng)求必須放在初始化的階段,比如在Activity.onCreate/onResume, 或者
View.onFinishInflate方法中。如果不這樣處理,那么如果app在請(qǐng)求過(guò)程中重啟的話(huà),權(quán)限請(qǐng)求結(jié)果將不會(huì)發(fā)送給訂閱者即subscriber。
2.整體介紹
RxPermission一共就只有三個(gè)類(lèi):Permission是定義的權(quán)限model類(lèi),用來(lái)存放權(quán)限名稱(chēng)以及是否獲取權(quán)限的信息。RxPermissions就是最主要的類(lèi)了,利用RxJava提供了我們上面在使用方法中介紹的所有方法。還有一個(gè)ShadowActivity類(lèi)是用來(lái)請(qǐng)求權(quán)限用的。下面我們就來(lái)詳細(xì)介紹RxPermission的實(shí)現(xiàn)。
注意:如果你還未了解過(guò)RxJava那么可以先閱讀本文最后的一系列優(yōu)秀文章。如果你已經(jīng)了解過(guò)RxJava的話(huà),那么下面我將會(huì)介紹RxJava中的部分操作符在RxPermission中的實(shí)際運(yùn)用。相信能幫助你更好的理解RxJava中操作符的使用。
3.源碼分析
我們依然按照我們慣用的方法來(lái)分析,通過(guò)使用方法來(lái)分析調(diào)用流程,最終理解整個(gè)項(xiàng)目。首先再回顧一遍使用方法(注意這里我同時(shí)請(qǐng)求了兩個(gè)權(quán)限),那么結(jié)果將是如果所有權(quán)限請(qǐng)求成功會(huì)返回true,若有一個(gè)權(quán)限未成功則返回false。代碼如下:
// 必須在初始化階段調(diào)用,例如onCreate()方法中
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(granted -> {
if (granted) { // 在android 6.0之前會(huì)默認(rèn)返回true
// 已經(jīng)獲取權(quán)限
} else {
// 未獲取權(quán)限
}
});
1.RxPermissions.getInstance(this)的實(shí)現(xiàn)
static RxPermissions sSingleton;
private Context :;
public static RxPermissions getInstance(Context) {
if (sSingleton == null) {
sSingleton = new RxPermissions(ctx.getApplicationContext());
}
return sSingleton;
}
RxPermissions(Context ctx) {
mCtx = ctx;
}
很明顯是維護(hù)RxPermissions的單例,不再多做介紹,緊接著我們來(lái)看RxPermissions中request(Manifest.permission.CAMERA)方法的實(shí)現(xiàn):
2.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)的實(shí)現(xiàn):
public Observable<Boolean> request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}
首先從返回值看到是返回一個(gè)Observable<Boolean>對(duì)象,方法中也是直接return了Observable.just(null).compose(ensure(permissions))。這里涉及兩個(gè)方法分別是just()以及compose()我們先解釋這兩個(gè)操作符:
1.Observable.just(null)
just操作符可以將某個(gè)對(duì)象轉(zhuǎn)化為Observable對(duì)象,并且將其發(fā)射出去,可以是一個(gè)數(shù)字、一個(gè)字符串、數(shù)組、Iterate對(duì)象等,是RxJava中非??旖莸膭?chuàng)建Observable對(duì)象的方法。在這里just()方法中雖然傳入的是null但是并不影響創(chuàng)建出的Observable的作用,如果有subscriber訂閱依然會(huì)依次調(diào)用其onNext()和onCompleted()方法。所以這里就是為了創(chuàng)建出一個(gè)Observable對(duì)象,便于后續(xù)的處理。創(chuàng)建完Observable對(duì)象之后緊接著調(diào)用了compose()方法:
2.compose(Transformer)操作符
compose()操作符是針對(duì)Observable自身的變換,通過(guò)我們自己定義的Transformer對(duì)象可以將對(duì)Observable對(duì)象變換的操作封裝起來(lái),實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Transformer對(duì)象如下:
class myTransformer implements Observable.Transformer<Object, Boolean> {
@Override
public Observable<Boolean> call(Observable<Object> objectObservable) {
return objectObservable.map(new Func1<Object, Boolean>() {
@Override
public Boolean call(Object o) {
return true;
}
});
}
}
通過(guò)上面這個(gè)Transformer就可以將任何Observable<Object>對(duì)象轉(zhuǎn)換成Observable<Boolean>對(duì)象了。當(dāng)然在Transformer里你也可以返回一個(gè)全新的Observable對(duì)象。RxPermissions就是這樣做的,那么回到項(xiàng)目中再來(lái)看compose(ensure(permissions));那么ensure(permissions);一定是返回一個(gè)Transformer對(duì)象了。
3.ensure(permissions);方法的實(shí)現(xiàn)
我們來(lái)看看ensure(permissions)方法的實(shí)現(xiàn):
public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
//創(chuàng)建一個(gè)Transformer對(duì)象返回
return new Observable.Transformer<Object, Boolean>() {
// o表示當(dāng)前Observable對(duì)象。
@Override
public Observable<Boolean> call(Observable<Object> o) {
//request(o, permissions) 方法返回 Observable<Permission>對(duì)象
return request(o, permissions)
// 將Observable<Permission>轉(zhuǎn)換為Observable<Boolean>
// buffer操作符
.buffer(permissions.length)
// flatMap操作符
.flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(List<Permission> permissions) {
// 如果permissions為空那么直接返回Observable.empty();
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();
}
// 遍歷所有Permission,如果有一個(gè)未成功則返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
}
};
}
確實(shí)是返回一個(gè)Observable.Transformer對(duì)象,那么在call()方法里首先調(diào)用了request(o, permissions)方法,然后又進(jìn)行了buffer()和flatMap()的處理,最終會(huì)返回Observable.empty();、Observable.just(false);或Observable.just(true);對(duì)象。我們先來(lái)看看request(o, permissions)方法的實(shí)現(xiàn):
4.request(o, permissions);方法的實(shí)現(xiàn)
private Observable<Permission> request(final Observable<?> trigger,
final String... permissions) {
//如果并沒(méi)有請(qǐng)求的權(quán)限則拋出異常
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 Func1<Object, Observable<Permission>>() {
@Override
public Observable<Permission> call(Object o) {
return request_(permissions);
}
});
}
首先對(duì)permissions做了判斷,然后調(diào)用了oneOf(trigger, pending(permissions))方法,并通過(guò)flatMap()操作符在call()方法中調(diào)用了request_(permissions);按照慣例我們應(yīng)該去看oneOf()方法的實(shí)現(xiàn)了。但是oneOf()方法里其實(shí)并沒(méi)有什么實(shí)際意義,看了項(xiàng)目的commit log我覺(jué)得應(yīng)該是歷史遺留問(wèn)題,作者可能是想處理一些相同重復(fù)的權(quán)限請(qǐng)求,但是并沒(méi)有實(shí)現(xiàn)。所以其實(shí)這個(gè)方法完全可以這樣代替;
private Observable<Permission> request(final Observable<?> trigger,
final String... permissions) {
return request_(permissions);
}
直接調(diào)用request_(permissions);即可。我測(cè)試中并沒(méi)有發(fā)現(xiàn)問(wèn)題。目前我還沒(méi)有聯(lián)系到作者詢(xún)問(wèn)這個(gè)方法的實(shí)現(xiàn)目的,稍后可能會(huì)提一個(gè)issue,如果有結(jié)果會(huì)在文章中更新。所以這里大家就完全可以理解成直接調(diào)用了request_(permissions);方法:
5.request_(permissions);方法的實(shí)現(xiàn)
@TargetApi(Build.VERSION_CODES.M)
private Observable<Permission> request_(final String... permissions) {
//創(chuàng)建出一個(gè)存放Observable<Permission>的list
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
//存放還為請(qǐng)求權(quán)限的list
List<String> unrequestedPermissions = new ArrayList<>();
// 在請(qǐng)求多個(gè)權(quán)限的時(shí)候,我們?yōu)槊恳粋€(gè)請(qǐng)求的權(quán)限都創(chuàng)建一個(gè)observable對(duì)象,在最后
// 這些observable會(huì)被合并成一個(gè)response。
for (String permission : permissions) {
log("Requesting permission " + permission);
//如果是已經(jīng)獲得的權(quán)限,或者Android版本在6.0之前則直接添加一個(gè)
// Observable.just(new Permission(permission, true))對(duì)象.
if (isGranted(permission)) {
// Already granted, or not Android M
// Return a granted Permission object.
list.add(Observable.just(new Permission(permission, true)));
continue;
}
// 如果是已經(jīng)拒絕的權(quán)限則添加
// Observable.just(new Permission(permission, false))對(duì)象.
if (isRevoked(permission)) {
// Revoked by a policy, return a denied Permission object.
list.add(Observable.just(new Permission(permission, false)));
continue;
}
PublishSubject<Permission> subject = mSubjects.get(permission);
// 如果mSubjects 不存在當(dāng)前 permission,則添加到unrequestedPermissions中
// 并且創(chuàng)建PublishSubject對(duì)象并添加到mSubjects中。
if (subject == null) {
unrequestedPermissions.add(permission);
subject = PublishSubject.create();
mSubjects.put(permission, subject);
}
//并且添加到list中
list.add(subject);
}
//如果有未請(qǐng)求的權(quán)限
if (!unrequestedPermissions.isEmpty()) {
startShadowActivity(unrequestedPermissions
.toArray(new String[unrequestedPermissions.size()]));
}
return Observable.concat(Observable.from(list));
}
void startShadowActivity(String[] permissions) {
log("startShadowActivity " + TextUtils.join(", ", permissions));
Intent intent = new Intent(mCtx, ShadowActivity.class);
intent.putExtra("permissions", permissions);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCtx.startActivity(intent);
}
代碼如上,注釋非常清晰,整體上就是將已經(jīng)允許的權(quán)限和已經(jīng)拒絕過(guò)的權(quán)限添加到list中,并且將還未請(qǐng)求的權(quán)限分別添加到mSubjects與list中。然后調(diào)用startShadowActivity();方法。最后通過(guò)Observable.concat(Observable.from(list));返回。這里主要包含PublishSubject與concat()操作符的知識(shí):
6.PublishSubject對(duì)象
從PublishSubject的文檔中,可以看出是繼承自Subject,Subject是既可以充當(dāng)Observer又能充當(dāng)Observable的。從文檔中的Example中可以看到訂閱PublishSubject的observer只會(huì)接收到訂閱之后PublishSubject發(fā)送的數(shù)據(jù),但是本項(xiàng)目中并沒(méi)有體現(xiàn)出此特性,主要是利用PublishSubject中的onNext()和onCompleted()方法,這里大致了解這么多,下面是Example的代碼:
PublishSubject<Object> subject = PublishSubject.create();
// observer1 will receive all onNext and onCompleted events
subject.subscribe(observer1);
subject.onNext("one");
subject.onNext("two");
// observer2 will only receive "three" and onCompleted
subject.subscribe(observer2);
subject.onNext("three");
subject.onCompleted();
7.concat()操作符
Concat操作符將多個(gè)Observable結(jié)合成一個(gè)Observable并發(fā)射數(shù)據(jù),并且嚴(yán)格按照先后順序發(fā)射數(shù)據(jù),前一個(gè)Observable的數(shù)據(jù)沒(méi)有發(fā)射完,是不能發(fā)射后面Observable的數(shù)據(jù)的。引用自此篇文章。所以在本項(xiàng)目中Concat()是為了保證請(qǐng)求的權(quán)限按順序返回。接下來(lái)我們看看ShadowActivity的實(shí)現(xiàn):
8.ShadowActivity的實(shí)現(xiàn)
@TargetApi(Build.VERSION_CODES.M)
public class ShadowActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
handleIntent(getIntent());
}
}
@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String[] permissions = intent.getStringArrayExtra("permissions");
requestPermissions(permissions, 42);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
RxPermissions.getInstance(this).onRequestPermissionsResult(requestCode, permissions, grantResults);
finish();
}
}
很簡(jiǎn)單其實(shí)就是按照系統(tǒng)提供給我們的方法進(jìn)行權(quán)限請(qǐng)求,最后回調(diào)RxPermissions的onRequestPermissionsResult()方法:
9.onRequestPermissionsResult()方法的實(shí)現(xiàn)
void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
for (int i = 0, size = permissions.length; i < size; i++) {
log("onRequestPermissionsResult " + permissions[i]);
// 取出對(duì)應(yīng)的PublishSubject對(duì)象
PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
if (subject == null) {
// No subject found
throw new IllegalStateException("RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
}
//從mSubjects移除
mSubjects.remove(permissions[i]);
//獲取結(jié)果
boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
//調(diào)用onNext()方法發(fā)送結(jié)果
subject.onNext(new Permission(permissions[i], granted));
//調(diào)用onCompleted()方法。
subject.onCompleted();
}
}
簡(jiǎn)單的來(lái)說(shuō)就是拿到結(jié)果并發(fā)送結(jié)果。所以就又回到了最初的ensure(permissions);方法中的request(o, permissions)之后,代碼如下:
request(o, permissions)
// 將Observable<Permission>轉(zhuǎn)換為Observable<Boolean>
// buffer操作符
.buffer(permissions.length)
// flatMap操作符
.flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(List<Permission> permissions) {
// 如果permissions為空那么直接返回Observable.empty();
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();
}
// 遍歷所有Permission,如果有一個(gè)未成功則返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
所以這里會(huì)不斷的發(fā)送Observable<Permission>對(duì)象,請(qǐng)求了幾個(gè)權(quán)限就會(huì)發(fā)送幾次,但是這里用了一個(gè)buffer()操作符,關(guān)于buffer()操作符:
10.buffer()操作符
buffer英文是緩沖區(qū)的意思。所以Buffer操作符所要做的事情就是將數(shù)據(jù)按照規(guī)定的大小做一下緩存,然后將緩存的數(shù)據(jù)作為一個(gè)集合發(fā)射出去。詳細(xì)可以看這里,所以在本項(xiàng)目中就是講這些Observable<Permission>轉(zhuǎn)換成Observable<List<Permission>>對(duì)象,緊接著又使用了flatMap()操作符然后返回了我們最終的結(jié)果。以上就是整個(gè)的調(diào)用流程了,如果有不清楚的建議可以多多的調(diào)試RxPermission的代碼以及查閱各種資料幫助理解。
11. requestEach()、ensureEach()、ensure()的實(shí)現(xiàn)
以上我們分析了request()方法的實(shí)現(xiàn),看似好像還剩下上面三個(gè)方法沒(méi)有分析。其實(shí)仔細(xì)看的同學(xué)應(yīng)該已經(jīng)看明白了。上面三個(gè)方法其實(shí)都是差不多的。如果你看懂了request()方法的實(shí)現(xiàn),那么這三個(gè)方法你一定能看懂,有興趣的同學(xué)可以自行去源碼里研究。
4.個(gè)人評(píng)價(jià)
其實(shí)Android 6.0的權(quán)限處理我自己在項(xiàng)目中都沒(méi)有使用過(guò),因?yàn)槟媚壳皣?guó)內(nèi)市場(chǎng)來(lái)說(shuō),首先Android 6.0的手機(jī)占有量非常少。再者我們可以使用很簡(jiǎn)單的方法將targetSdkVersion設(shè)置為22來(lái)兼容6.0的權(quán)限處理。所以目前項(xiàng)目中應(yīng)該很少需要使用RxPermissions這個(gè)項(xiàng)目。但是這個(gè)項(xiàng)目作為RxJava的學(xué)習(xí)資料是相當(dāng)?shù)暮?。從中我們可以學(xué)到大量的RxJava相關(guān)的使用知識(shí)。如果你現(xiàn)在在學(xué)習(xí)RxJava,強(qiáng)烈推薦這個(gè)項(xiàng)目。
5.RxJava相關(guān)文章
RxJava
深入淺出RxJava(大頭鬼教父的文章)
給 Android 開(kāi)發(fā)者的 RxJava 詳解
RxJava 與 Retrofit 結(jié)合的最佳實(shí)踐
RxJava的操作符(有四篇)
RxJava操作符 (有八篇)
那些年我們錯(cuò)過(guò)的響應(yīng)式編程
不要打破鏈?zhǔn)剑菏褂肦xjava的compose()操作符
當(dāng)EventBus遇上RxJava
Rx小鄧子的簡(jiǎn)書(shū)相當(dāng)多RxJava相關(guān)文章
我每周會(huì)寫(xiě)一篇源代碼分析的文章,以后也可能會(huì)有其他主題.
如果你喜歡我寫(xiě)的文章的話(huà),歡迎關(guān)注我的新浪微博@達(dá)達(dá)達(dá)達(dá)sky
地址: http://weibo.com/u/2030683111
每周我會(huì)第一時(shí)間在微博分享我寫(xiě)的文章,也會(huì)積極轉(zhuǎn)發(fā)更多有用的知識(shí)給大家.