安卓 6.0獲取位置權(quán)限造成閃退

安卓 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 項目地址:RxPermissions,本文分析版本: 4c4d4e1 1.簡介 RxPermissions是基于R...
    SkyKai閱讀 13,325評論 20 63
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 大概因為我也是一個中國人,對南京大屠殺開始到中國成立的那段歷史追了很久,我想了解更多關(guān)于從前的故事,這樣自己才知道...
    哆啦A夢暗戀紅雨閱讀 378評論 0 8
  • 現(xiàn)在是周六晚上十一點,我剛剛在譯言網(wǎng)上發(fā)表了自己的第一篇試譯稿《美國煽動者——紐約客》,現(xiàn)在還在審核中。 我大學(xué)讀...
    耿涌閱讀 2,995評論 3 2

友情鏈接更多精彩內(nèi)容