安卓 6.0獲取位置權(quán)限造成閃退 開發(fā)用下面的方法解決的。
1.簡介
RxPermissions是基于RxJava開發(fā)的用于幫助在Android 6.0中處理運行時權(quán)限檢測的框架。在Android 6.0中,系統(tǒng)新增了部分權(quán)限的運行時動態(tài)獲取。而不再是在以前的版本中安裝的時候授予權(quán)限。
對于運行時的權(quán)限獲取提示,國內(nèi)的Android工程師們應(yīng)該并不陌生,國內(nèi)的第三方ROM例如MIUI在很早前就做了類似的功能。但是第三方ROM并不能提供給我們權(quán)限請求成功或失敗的接口,這就導(dǎo)致我們無法通過PackageManager提供的checkPermission()方法來準(zhǔn)確的獲取到我們是否獲得該權(quán)限。只能根據(jù)具體的權(quán)限來做相應(yīng)的處理。但是在Android 6.0中我們可以準(zhǔn)確的獲取我們的應(yīng)用是否獲取某個權(quán)限,具體的方法希望大家看這篇文章:Android 6.0 運行時權(quán)限處理。大致方法是通過API 23中的Activity的requestPermissions(String[] permissions, int requestCode);方法請求權(quán)限,并在onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults)回調(diào)方法中處理請求結(jié)果。這個方法與onActivityResult()類似。RxPermissions在這個基礎(chǔ)上做了封裝,使我們在處理運行時權(quán)限變得更加的簡單。
另外關(guān)于RxJava如果你現(xiàn)在還沒了解過RxJava可以直接翻到文章最下面去查看我總結(jié)的一些RxJava相關(guān)的文章,不然并不推薦直接看這篇文章。下面我們就來具體看看RxPermissions的使用方法以及源碼分析。
2.使用方法
1.直接獲取權(quán)限(使用Retrolambda使代碼更加簡潔,當(dāng)然并不是必須使用):
// 必須在初始化階段調(diào)用,例如onCreate()方法中
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(granted -> {
if (granted) { // 在android 6.0之前會默認(rèn)返回true
// 已經(jīng)獲取權(quán)限
} else {
// 未獲取權(quán)限
}
});
```
2.通過條件觸發(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被點擊的時候觸發(fā)獲取權(quán)限
});
3.一次請求多個權(quán)限(有兩種方式)
如果同時請求多個權(quán)限,下面這種方式會合并請求結(jié)果,即所有權(quán)限請求成功會返回true,若有一個權(quán)限未成功則返回false。
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(granted -> {
if (granted) {
// 所有權(quán)限請求被同意
} else {
// 至少有一個權(quán)限沒同意
}
});
當(dāng)然你可以通過requestEach or ensureEach 來分別獲取每一個權(quán)限請求的結(jié)果。
RxPermissions.getInstance(this)
.requestEach(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(permission -> { // 會發(fā)送兩個Permission對象
if (permission.granted) {
// permission.name is granted !
}
});
注意:由于在請求權(quán)限的過程中app有可能會被重啟,所以權(quán)限請求必須放在初始化的階段,比如在Activity.onCreate/onResume, 或者
View.onFinishInflate方法中。如果不這樣處理,那么如果app在請求過程中重啟的話,權(quán)限請求結(jié)果將不會發(fā)送給訂閱者即subscriber。
2.整體介紹
RxPermission一共就只有三個類:Permission是定義的權(quán)限model類,用來存放權(quán)限名稱以及是否獲取權(quán)限的信息。RxPermissions就是最主要的類了,利用RxJava提供了我們上面在使用方法中介紹的所有方法。還有一個ShadowActivity類是用來請求權(quán)限用的。下面我們就來詳細介紹RxPermission的實現(xiàn)。
注意:如果你還未了解過RxJava那么可以先閱讀本文最后的一系列優(yōu)秀文章。如果你已經(jīng)了解過RxJava的話,那么下面我將會介紹RxJava中的部分操作符在RxPermission中的實際運用。相信能幫助你更好的理解RxJava中操作符的使用。
3.源碼分析
我們依然按照我們慣用的方法來分析,通過使用方法來分析調(diào)用流程,最終理解整個項目。首先再回顧一遍使用方法(注意這里我同時請求了兩個權(quán)限),那么結(jié)果將是如果所有權(quán)限請求成功會返回true,若有一個權(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之前會默認(rèn)返回true
// 已經(jīng)獲取權(quán)限
} else {
// 未獲取權(quán)限
}
});
1.RxPermissions.getInstance(this)的實現(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;
}
很明顯是維護RxPermissions的單例,不再多做介紹,緊接著我們來看RxPermissions中request(Manifest.permission.CAMERA)方法的實現(xiàn):
2.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)的實現(xiàn):
public Observable<Boolean> request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}
首先從返回值看到是返回一個Observable<Boolean>對象,方法中也是直接return了Observable.just(null).compose(ensure(permissions))。這里涉及兩個方法分別是just()以及compose()我們先解釋這兩個操作符:
1.Observable.just(null)
just操作符可以將某個對象轉(zhuǎn)化為Observable對象,并且將其發(fā)射出去,可以是一個數(shù)字、一個字符串、數(shù)組、Iterate對象等,是RxJava中非常快捷的創(chuàng)建Observable對象的方法。在這里just()方法中雖然傳入的是null但是并不影響創(chuàng)建出的Observable的作用,如果有subscriber訂閱依然會依次調(diào)用其onNext()和onCompleted()方法。所以這里就是為了創(chuàng)建出一個Observable對象,便于后續(xù)的處理。創(chuàng)建完Observable對象之后緊接著調(diào)用了compose()方法:
2.compose(Transformer)操作符
compose()操作符是針對Observable自身的變換,通過我們自己定義的Transformer對象可以將對Observable對象變換的操作封裝起來,實現(xiàn)一個簡單的Transformer對象如下:
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;
}
});
}
}
通過上面這個Transformer就可以將任何Observable<Object>對象轉(zhuǎn)換成Observable<Boolean>對象了。當(dāng)然在Transformer里你也可以返回一個全新的Observable對象。RxPermissions就是這樣做的,那么回到項目中再來看compose(ensure(permissions));那么ensure(permissions);一定是返回一個Transformer對象了。
3.ensure(permissions);方法的實現(xiàn)
我們來看看ensure(permissions)方法的實現(xiàn):
public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
//創(chuàng)建一個Transformer對象返回
return new Observable.Transformer<Object, Boolean>() {
// o表示當(dāng)前Observable對象。
@Override
public Observable<Boolean> call(Observable<Object> o) {
//request(o, permissions) 方法返回 Observable<Permission>對象
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,如果有一個未成功則返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
}
};
}
確實是返回一個Observable.Transformer對象,那么在call()方法里首先調(diào)用了request(o, permissions)方法,然后又進行了buffer()和flatMap()的處理,最終會返回Observable.empty();、Observable.just(false);或Observable.just(true);對象。我們先來看看request(o, permissions)方法的實現(xiàn):
4.request(o, permissions);方法的實現(xiàn)
private Observable<Permission> request(final Observable<?> trigger,
final String... permissions) {
//如果并沒有請求的權(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);
}
});
}
首先對permissions做了判斷,然后調(diào)用了oneOf(trigger, pending(permissions))方法,并通過flatMap()操作符在call()方法中調(diào)用了request_(permissions);按照慣例我們應(yīng)該去看oneOf()方法的實現(xiàn)了。但是oneOf()方法里其實并沒有什么實際意義,看了項目的commit log我覺得應(yīng)該是歷史遺留問題,作者可能是想處理一些相同重復(fù)的權(quán)限請求,但是并沒有實現(xiàn)。所以其實這個方法完全可以這樣代替;
private Observable<Permission> request(final Observable<?> trigger,
final String... permissions) {
return request_(permissions);
}
直接調(diào)用request_(permissions);即可。我測試中并沒有發(fā)現(xiàn)問題。目前我還沒有聯(lián)系到作者詢問這個方法的實現(xiàn)目的,稍后可能會提一個issue,如果有結(jié)果會在文章中更新。所以這里大家就完全可以理解成直接調(diào)用了request_(permissions);方法:
5.request_(permissions);方法的實現(xiàn)
@TargetApi(Build.VERSION_CODES.M)
private Observable<Permission> request_(final String... permissions) {
//創(chuàng)建出一個存放Observable<Permission>的list
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
//存放還為請求權(quán)限的list
List<String> unrequestedPermissions = new ArrayList<>();
// 在請求多個權(quán)限的時候,我們?yōu)槊恳粋€請求的權(quán)限都創(chuàng)建一個observable對象,在最后
// 這些observable會被合并成一個response。
for (String permission : permissions) {
log("Requesting permission " + permission);
//如果是已經(jīng)獲得的權(quán)限,或者Android版本在6.0之前則直接添加一個
// Observable.just(new Permission(permission, true))對象.
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))對象.
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對象并添加到mSubjects中。
if (subject == null) {
unrequestedPermissions.add(permission);
subject = PublishSubject.create();
mSubjects.put(permission, subject);
}
//并且添加到list中
list.add(subject);
}
//如果有未請求的權(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)拒絕過的權(quán)限添加到list中,并且將還未請求的權(quán)限分別添加到mSubjects與list中。然后調(diào)用startShadowActivity();方法。最后通過Observable.concat(Observable.from(list));返回。這里主要包含PublishSubject與concat()操作符的知識:
6.PublishSubject對象
從PublishSubject的文檔中,可以看出是繼承自Subject,Subject是既可以充當(dāng)Observer又能充當(dāng)Observable的。從文檔中的Example中可以看到訂閱PublishSubject的observer只會接收到訂閱之后PublishSubject發(fā)送的數(shù)據(jù),但是本項目中并沒有體現(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操作符將多個Observable結(jié)合成一個Observable并發(fā)射數(shù)據(jù),并且嚴(yán)格按照先后順序發(fā)射數(shù)據(jù),前一個Observable的數(shù)據(jù)沒有發(fā)射完,是不能發(fā)射后面Observable的數(shù)據(jù)的。引用自此篇文章。所以在本項目中Concat()是為了保證請求的權(quán)限按順序返回。接下來我們看看ShadowActivity的實現(xiàn):
8.ShadowActivity的實現(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();
}
}
很簡單其實就是按照系統(tǒng)提供給我們的方法進行權(quán)限請求,最后回調(diào)RxPermissions的onRequestPermissionsResult()方法:
9.onRequestPermissionsResult()方法的實現(xiàn)
void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
for (int i = 0, size = permissions.length; i < size; i++) {
log("onRequestPermissionsResult " + permissions[i]);
// 取出對應(yīng)的PublishSubject對象
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é)果并發(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,如果有一個未成功則返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
所以這里會不斷的發(fā)送Observable<Permission>對象,請求了幾個權(quán)限就會發(fā)送幾次,但是這里用了一個buffer()操作符,關(guān)于buffer()操作符:
10.buffer()操作符
buffer英文是緩沖區(qū)的意思。所以Buffer操作符所要做的事情就是將數(shù)據(jù)按照規(guī)定的大小做一下緩存,然后將緩存的數(shù)據(jù)作為一個集合發(fā)射出去。詳細可以看這里,所以在本項目中就是講這些Observable<Permission>轉(zhuǎn)換成Observable<List<Permission>>對象,緊接著又使用了flatMap()操作符然后返回了我們最終的結(jié)果。以上就是整個的調(diào)用流程了,如果有不清楚的建議可以多多的調(diào)試RxPermission的代碼以及查閱各種資料幫助理解。
11. requestEach()、ensureEach()、ensure()的實現(xiàn)
以上我們分析了request()方法的實現(xiàn),看似好像還剩下上面三個方法沒有分析。其實仔細看的同學(xué)應(yīng)該已經(jīng)看明白了。上面三個方法其實都是差不多的。如果你看懂了request()方法的實現(xiàn),那么這三個方法你一定能看懂,有興趣的同學(xué)可以自行去源碼里研究。
4.個人評價
其實Android 6.0的權(quán)限處理我自己在項目中都沒有使用過,因為拿目前國內(nèi)市場來說,首先Android 6.0的手機占有量非常少。再者我們可以使用很簡單的方法將targetSdkVersion設(shè)置為22來兼容6.0的權(quán)限處理。所以目前項目中應(yīng)該很少需要使用RxPermissions這個項目。但是這個項目作為RxJava的學(xué)習(xí)資料是相當(dāng)?shù)暮?。從中我們可以學(xué)到大量的RxJava相關(guān)的使用知識。如果你現(xiàn)在在學(xué)習(xí)RxJava,強烈推薦這個項目。
原文來自:
http://geek.csdn.net/news/detail/68303