Android開發(fā)之深度項目設計探索(三)

本文是《Android開發(fā)之深度項目設計探索》系列的第三篇滑燃,主要介紹的是 基于最新RxPermissions 類庫的使用及源碼分析拱雏,本系列歷史文章:
《Android開發(fā)之深度項目設計探索(一)》
《Android開發(fā)之深度項目設計探索(二)》
Permission屏歹,這個單詞翻譯過來的意思有:允許狡相、許可准颓、權限镣屹。我們Android開發(fā)親切的將其稱為權限序芦。

權限是一種安全機制戚哎。Android權限機制主要用于:限制應用程序內(nèi)部某些具有限制特性的功能使用以及應用程序之間的組件訪問亡资。

在Android系統(tǒng)6.0版本(也就是 SdkVersion = 23)之前,權限的聲明僅需要在清單配置文件中板驳,通過標簽uses-permission來聲明應用,也就意味所需要的權限碍拆,如:

    <uses-permission android:name="android.permission.CAMERA"/>

這行配置代碼的意思意味著該應用允許使用CAMERA(照相機)權限若治;

由于時代的發(fā)展以及各種因素慨蓝,谷歌Android技術團隊出于安全角度這一原則設計考慮,在Android系統(tǒng)6.0版本開始直砂,之后的版本提出了一些新概念菌仁,整理下來有以下幾點:
概念一:權限分為兩種、一種是普通權限静暂;還有一種是危險權限
概念二:普通權限济丘,直接在清單文件中配置聲明即可
概念三:危險權限,谷歌覺得部分權限涉及到用戶的隱私洽蛀,因此必須明確告知用戶應用需要那些權限摹迷,但是這樣的危險權限需要用戶手動授權

既然谷歌在新版本給我們帶來了新概念和需要解決的問題,那么就需要解決這個權限問題:

  • 解決方式一:暫時不適配Android6.0系統(tǒng)版本郊供,也就是將targetSdkVersion降到23以上(這種法子算曲線救國)
  • 解決方式二:直接適配危險權限峡碉,主動告知用戶打開權限

那么這篇文章介紹的 RxPermissions-官方文檔,這是一款基于Rxjava2的運行時權限解決方案驮审,

  • 這個庫的minSdkVersion 最低不能低于11鲫寄,也就是 "use this library your minSdkVersion must be >= 11"
  • 添加Maven支持
allprojects {
    repositories {
        ...
        //添加如下支持
        maven { url 'https://jitpack.io' }
    }
}
  • 在gradle導入最新版本的RxPermissions依賴:
implementation 'com.github.tbruyelle:rxpermissions:0.10.2

集成和使用步驟如下:

  • 首先:創(chuàng)建RxPermissions實例:
 //這里的this就是Activity or Fragment instance
 RxPermissions rxPermissions = new RxPermissions(this);
  • 接著:代碼使用,需搭配Rxjava2使用疯淫,寫法如下:
A:獲取單個權限

        RxPermissions rxPermissions = new RxPermissions(this);

//      申請單個權限
        rxPermissions.request(Manifest.permission.CAMERA).subscribe(new Consumer<Boolean>() {
            @Override
            public void accept(Boolean aBoolean) throws Exception {
                if (aBoolean == true){
//              用戶已經(jīng)同意該權限
                    Log.i(TAG, "accept: CAMERA 權限成功");
                }else {
//              用戶拒絕權限
                    Log.i(TAG, "accept: CAMERA 權限失敗");
                }
            }
        });
B:獲取多個權限

獲取多個權限方式有兩種寫法:

  • 寫法一:
//      用戶同時申請多個權限地来,方式一:
//      如果用戶全部申請成功,才會返回true
//      如果用戶有一個權限拒絕申請熙掺,那么就會返回失敗
        rxPermissions.requestEach(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_CALENDAR,
                Manifest.permission.READ_PHONE_STATE).
                subscribe(new Consumer<Permission>() {
            @Override
            public void accept(Permission permission) throws Exception {
                if (permission.granted) {
                    // 用戶已經(jīng)同意該權限
                    Log.d(TAG, permission.name + "用戶授予權限");
                } else if (permission.shouldShowRequestPermissionRationale) {
                    // 用戶拒絕了該權限未斑,沒有選中『不再詢問』(Never ask again),那么下次再次啟動時,還會提示請求權限的對話框
                    Log.d(TAG, permission.name + "用戶拒絕權限——下次啟動可以繼續(xù)申請");
                } else {
                    // 1壹ā@唷!注意@铝汀Q客弧!
                    // 用戶拒絕了該權限费就,并且選中了『不再詢問』
                    // 需要去 APP設置 里面去打開對應的申請權限
                    Log.d(TAG, permission.name + "用戶拒絕權限——勾選了不在詢問——提示用戶后續(xù)去手動申請");
                }
            }
        });

  • 寫法二:
//      用戶同時申請多個權限诉瓦,方式二:
//      對應每個權限申請的操作
//      思考:
//      我們可以在應用啟動之前也打開獲取權限,然后記錄每個權限是否申請成功力细,記錄的方式可以通過sp存儲狀態(tài)
//      在需要權限的時候睬澡,首先判斷SP存儲的狀態(tài)是否為權限授權成功,如果沒有授權成功眠蚂,那么就再次請求授權
        rxPermissions.requestEachCombined(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_CALENDAR,
                Manifest.permission.READ_PHONE_STATE).
                subscribe(new Consumer<Permission>() {
            @Override
            public void accept(Permission permission) throws Exception {
//              判斷具體的對應權限

                if (permission.name.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    if (permission.granted){
                        Log.i(TAG, "WRITE_EXTERNAL_STORAGE_accept: 權限申請成功");
                    }else {
                        Log.i(TAG, "WRITE_EXTERNAL_STORAGE_accept: 權限授權失敗");
                    }
                }

                if (permission.name.equals(Manifest.permission.READ_PHONE_STATE)) {
                    if (permission.granted){
                        Log.i(TAG, "READ_PHONE_STATE: 權限申請成功");
                    }else {
                        Log.i(TAG, "READ_PHONE_STATE: 權限授權失敗");
                    }
                }
            }
        });

嗯煞聪,沒錯,基本上逝慧,這款框架的使用就已經(jīng)介紹完了昔脯,是不是很簡單優(yōu)雅啄糙。
但是有一些細節(jié)我們需要注意,如該庫的作者不建議我們在 onResume()這個生命周期里面進行申請權限云稚, RxPermissions-官方文檔 也給了詳細說明隧饼,

下面是源碼解讀

Rxpermissions源碼分析:
使用一:

我們知道,Rxpermissions使用的第一步是在Activity中創(chuàng)建RxPermissions這個實例化對象静陈。實際上燕雁,RxPermission采用的方式是利用了一個隱形的Fragment來請求權限,然后在回調(diào)中用RxJava進行數(shù)據(jù)的組裝和轉化鲸拥,最后變成了布爾類型的數(shù)據(jù)回調(diào)回來拐格。這個隱形的Fragment,就是類庫中的RxPermissionsFragment刑赶,

public class RxPermissionsFragment extends Fragment {

    private static final int PERMISSIONS_REQUEST_CODE = 42;

    // Contains all the current permission requests.
    // Once granted or denied, they are removed from it.
    private Map<String, PublishSubject<Permission>> mSubjects = new HashMap<>();
    private boolean mLogging;

    public RxPermissionsFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @TargetApi(Build.VERSION_CODES.M)
    void requestPermissions(@NonNull String[] permissions) {
        requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
    }

    @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 subject
            PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
            if (subject == null) {
                // No subject found
                Log.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();
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    boolean isGranted(String permission) {
        final FragmentActivity fragmentActivity = getActivity();
        if (fragmentActivity == null) {
            throw new IllegalStateException("This fragment must be attached to an activity.");
        }
        return fragmentActivity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

    @TargetApi(Build.VERSION_CODES.M)
    boolean isRevoked(String permission) {
        final FragmentActivity fragmentActivity = getActivity();
        if (fragmentActivity == null) {
            throw new IllegalStateException("This fragment must be attached to an activity.");
        }
        return fragmentActivity.getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
    }

    public void setLogging(boolean logging) {
        mLogging = logging;
    }

    public PublishSubject<Permission> getSubjectByPermission(@NonNull String permission) {
        return mSubjects.get(permission);
    }

    public boolean containsByPermission(@NonNull String permission) {
        return mSubjects.containsKey(permission);
    }

    public void setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject<Permission> subject) {
        mSubjects.put(permission, subject);
    }

    void log(String message) {
        if (mLogging) {
            Log.d(RxPermissions.TAG, message);
        }
    }

}

可以看到捏浊,這個類繼承了Fragment。然后在onCreate中并沒有創(chuàng)建具體的UI布局撞叨,我們知道金踪。Fragment具有屬性retainInstance,默認值為false牵敷。 當設備旋轉時热康,fragment會隨托管activity一起銷毀并重建。解決辦法是:調(diào)用setRetainInstance(true)方法可保留fragment不會重新創(chuàng)建(例如旋轉屏幕)

你可能會問劣领,那這個隱形的Fragment又是在那里創(chuàng)建的?答案:創(chuàng)建的時機铁材,是在創(chuàng)建RxPermissions的對象中尖淘,順帶創(chuàng)建了這個實例對象(通過構造方法實現(xiàn)):下面是Rxpermissions這個類的構造方法以及部分函數(shù)源碼:

public class RxPermissions {

    static final String TAG = RxPermissions.class.getSimpleName();
    static final Object TRIGGER = new Object();

    @VisibleForTesting
    Lazy<RxPermissionsFragment> mRxPermissionsFragment;

    public RxPermissions(@NonNull final FragmentActivity activity) {
        mRxPermissionsFragment = getLazySingleton(activity.getSupportFragmentManager());
    }

    public RxPermissions(@NonNull final Fragment fragment) {
        mRxPermissionsFragment = getLazySingleton(fragment.getChildFragmentManager());
    }

    @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;
            }

        };
    }

    private RxPermissionsFragment getRxPermissionsFragment(@NonNull final FragmentManager fragmentManager) {
        RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(fragmentManager);
        boolean isNewInstance = rxPermissionsFragment == null;
        if (isNewInstance) {
            rxPermissionsFragment = new RxPermissionsFragment();
            fragmentManager
                    .beginTransaction()
                    .add(rxPermissionsFragment, TAG)
                    .commitNow();
        }
        return rxPermissionsFragment;
    }
//.............省略部分源碼................
}

這個隱形的Fragment很重要,因為權限的申請與申請結果的回調(diào)都是在Fragment中完成的著觉〈迳基于此,開發(fā)人員才不需要為申請結果重寫回調(diào)方法饼丘。

使用二:

接著趁桃,是Rxpermissions基本使用的代碼,也就是rxPermissions.request......肄鸽,對應的源碼如下:


    /**
     * Request permissions immediately, <b>must be invoked during initialization phase
     * of your application</b>.
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensure(permissions));
    }

    /**
     * Request permissions immediately, <b>must be invoked during initialization phase
     * of your application</b>.
     */
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Observable<Permission> requestEach(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensureEach(permissions));
    }

    /**
     * Request permissions immediately, <b>must be invoked during initialization phase
     * of your application</b>.
     */
    public Observable<Permission> requestEachCombined(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensureEachCombined(permissions));
    }

從中可以看到卫病,上面使用的三種寫法,最終是調(diào)到了

Observable.just(TRIGGER).compose(ensureEach(permissions));
  • 步驟一:
    這里的 Just 操作符典徘,簡單點理解就是可以將某個對象轉化為Observable對象蟀苛。Just操作符,是RxJava中非炒澹快捷的創(chuàng)建Observable對象的方法帜平。如果通過just操作符創(chuàng)建了一個Observable幽告,繼續(xù)使用subscriber訂閱則會依次調(diào)用其onNext()和onCompleted()方法。這里的TRIGGER裆甩,源碼里面就是一個Object對象冗锁。
    static final Object TRIGGER = new Object();

通過源碼可以看到,根據(jù)just操作符創(chuàng)建完Observable對象之后緊接著調(diào)用了compose()方法嗤栓。
compose()操作符主要是將一個Observable對象(具體的數(shù)據(jù)類型)轉換成另一個Observable(對應的數(shù)據(jù)類型)對象冻河,我們發(fā)現(xiàn)此方法最終調(diào)用的是ensure(permissions)這個方法

  • 步驟二:
    ensure(permissions)這個方法的源碼如下:
    public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {

        ObservableTransformer<T, Boolean> observableTransformer = new ObservableTransformer<T, Boolean>() {
            @Override
            public ObservableSource<Boolean> apply(Observable<T> o) {

                Observable<Boolean> booleanObservable = 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()) {
                                    // 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);
                            }
                        });
                return booleanObservable;
            }
        };

        return observableTransformer;
    }

這個buffer()的操作符作用是什么呢,它的作用是為了將一個序列的Observable<Permission>對象轉換成Observable<List<Permission>>對象抛腕,

拋開Rxjava2常規(guī)的操作符除外芋绸,首先看下這段來自RxPermissions類庫部分源碼提供的自定義方法有:request(o, permissions);源碼還自定義了一個類:Permission担敌,其中p.granted這種寫法貌似就是類名.成員變量的操作摔敛,那我們就先看一眼Permission這個最基本的Java類
Permission源碼如下:

public class Permission {
    public final String name;
    public final boolean granted;
    public final boolean shouldShowRequestPermissionRationale;

    public Permission(String name, boolean granted) {
        this(name, granted, false);
    }

    public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) {
        this.name = name;
        this.granted = granted;
        this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale;
    }

    public Permission(List<Permission> permissions) {
        name = combineName(permissions);
        granted = combineGranted(permissions);
        shouldShowRequestPermissionRationale = combineShouldShowRequestPermissionRationale(permissions);
    }

    @Override
    @SuppressWarnings("SimplifiableIfStatement")
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        final Permission that = (Permission) o;

        if (granted != that.granted) return false;
        if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale)
            return false;
        return name.equals(that.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + (granted ? 1 : 0);
        result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Permission{" +
                "name='" + name + '\'' +
                ", granted=" + granted +
                ", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale +
                '}';
    }

    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();
    }

    private Boolean combineGranted(List<Permission> permissions) {
        return Observable.fromIterable(permissions)
                .all(new Predicate<Permission>() {
                    @Override
                    public boolean test(Permission permission) throws Exception {
                        return permission.granted;
                    }
                }).blockingGet();
    }

    private Boolean combineShouldShowRequestPermissionRationale(List<Permission> permissions) {
        return Observable.fromIterable(permissions)
                .any(new Predicate<Permission>() {
                    @Override
                    public boolean test(Permission permission) throws Exception {
                        return permission.shouldShowRequestPermissionRationale;
                    }
                }).blockingGet();
    }
}

這個類我們先放在這里,在看一眼類庫自定義的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");
        }
        Observable<Permission> permissionObservable =
                oneOf(trigger, pending(permissions))
                        .flatMap(new Function<Object, Observable<Permission>>() {
                    @Override
                    public Observable<Permission> apply(Object o) {
                        return requestImplementation(permissions);
                    }
                });

        return permissionObservable;

    }

可以看到這個方法返回的對象是一個Observable< Permission >全封,內(nèi)部邏輯首先判斷permissions 是否為null马昙,長度是否大于0,如果滿足其中一個條件就拋異常刹悴;如果permissions 不為null行楞,長度且大于0,在調(diào)用oneOf方法和pending( )方法來創(chuàng)建合并Observable對象土匀。其中子房,oneOf()和pending( )方法的函數(shù)如下:


    private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
        if (trigger == null) {
            return Observable.just(TRIGGER);
        }
        return Observable.merge(trigger, pending);
    }
    
    private Observable<?> pending(final String... permissions) {
        for (String p : permissions) {
            if (!mRxPermissionsFragment.get().containsByPermission(p)) {
                return Observable.empty();
            }
        }
        return Observable.just(TRIGGER);
    }

pending(final String... permissions)這個函數(shù)的主要功能有:

  • 首先循環(huán)遍歷,查詢該權限是否已經(jīng)在申請過了
  • 如果列表中有一個權限沒有在RxPermissionsFragment的HashMap集合中保存就轧,
    就返回Observeble.empty()证杭,返回的這個Observable對象會調(diào)用onComplete()方法,所以并不會進入flatMap

oneOf(Observable<?> trigger, Observable<?> pending)這個函數(shù)的主要是:

  • 首先判斷trigger對象是否為null妒御,如果為空解愤,則通過Just操作符創(chuàng)建一個Observable
  • 最后返回合并的Observable對象。

我們知道乎莉,request(o, permissions)最終是調(diào)用了requestImplementation(permissions);這個方法送讲,下面就看下requestImplementation(permissions);方法的內(nèi)部源碼:

  @TargetApi(Build.VERSION_CODES.M)
    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.get().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.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));
    }
這段代碼的主要意思有以下幾個方面(按照順序):

A:創(chuàng)建第一個集合(也就是源碼的List<Observable<Permission>> list)保存已經(jīng)申請的權限
B:創(chuàng)建第二個集合(也就是List<String> unrequestedPermissions)用來保存沒有請求成功的權限
C:對具體申請的權限列表進行循環(huán)遍歷:

  • 如果權限已經(jīng)申請過了,則直接保存到集合中(if (isGranted(permission)) {...}
  • 如果權限被撤銷惋啃,則將其作為申請被拒絕的權限保存到集合中:if (isRevoked(permission)) {...}

D:在RxPermissionsFragment中哼鬓,尋找是否已經(jīng)存在對應的權限,如果不存在(subject == null)边灭,則創(chuàng)建PublishSubject對象魄宏,將其添加到unrequestedPermissions集合中,然后將subject對象保存在第一個集合中存筏,
E:如果有未申請的權限宠互,則進行權限的申請操作味榛,也就是requestPermissionsFromFragment(unrequestedPermissionsArray);這個函數(shù)

F:利用第一個集合創(chuàng)建Observable對象,用concat操作符進行鏈接予跌,最終返回一個Observable<Permission>對象

結論:
這個方法主要的操作就是搏色,定義集合保存一次申請的所有權限。無論這個權限是已經(jīng)申請券册,還是被撤銷频轿,還是未申請,最終都會保存到list這個集合中烁焙。在后續(xù)的操作中航邢,才可以進行轉換。

同時骄蝇,定義集合用于保存未申請的權限膳殷,然后在循環(huán)結束之后進行未申請權限的申請九火。

因此,現(xiàn)在的關鍵就是requestPermissionsFromFragment(unrequestedPermissionsArray);這個函數(shù)岔激,源碼繼續(xù)跟進:

   @TargetApi(Build.VERSION_CODES.M)
    void requestPermissionsFromFragment(String[] permissions) {
        mRxPermissionsFragment.get().log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
        mRxPermissionsFragment.get().requestPermissions(permissions);
    }

繼續(xù)跟進:

@TargetApi(Build.VERSION_CODES.M)
    void requestPermissions(@NonNull String[] permissions) {
        requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
    }

看到這里,最終是調(diào)用了requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);辱匿,這個PERMISSIONS_REQUEST_CODE,的值是42炫彩,源碼里面有掀鹅。這個方法是Fragment提供的系統(tǒng)方法,那么回調(diào)的處理結果理所當然在RxPermissionsFragment中:

   @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 subject
            PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
            if (subject == null) {
                // No subject found
                Log.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();
        }
    }

這段回調(diào)處理的源碼意思主要有:
A:首先判斷請求碼媒楼,如果請求碼不等于42,則直接返回
B:以權限列表的長度為size戚丸,創(chuàng)建一個boolean數(shù)組
C:循環(huán)遍歷划址,看權限是否被永久拒絕了。這里會調(diào)用Fragment中的shouldShowRequestPermissionRationale()方法限府。這個方法如果權限申請成功會返回true夺颤;用戶點擊了不在提醒,且拒絕權限時胁勺,會返回false世澜。
D:調(diào)用下面的重載方法

進入重載方法以后的操作有:
A:對權限列表進行循環(huán)操作。尋找相對應的:subject署穗,也就是( PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
B:如果沒有對應的subject寥裂,則直接返回
C:如果有對應的subject嵌洼,首先將集合中的permission的PublishSubject對象進行移除;接著判斷是否申請成功封恰;最后執(zhí)行onNext( )返回相應的Permission對象

基本的源碼分析就到這了麻养。

評語:可以看到,RxPermissions這個庫的主要邏輯就是在通過隱形的Fragment以及Rxjava2的操作符來進行實踐的诺舔,通過合理搭配才讓這個庫使用起來比較方便鳖昌。

如果這篇文章對您有開發(fā)or學習上的些許幫助,希望各位看官留下寶貴的star低飒,謝謝许昨。

Ps:著作權歸作者所有,轉載請注明作者, 商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處(開頭或結尾請?zhí)砑愚D載出處褥赊,添加原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果糕档。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市崭倘,隨后出現(xiàn)的幾起案子翼岁,更是在濱河造成了極大的恐慌,老刑警劉巖琅坡,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榆俺,死亡現(xiàn)場離奇詭異茴晋,居然都是意外死亡诺擅,警方通過查閱死者的電腦和手機烁涌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門撮执,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舷丹,“玉大人,你說我怎么就攤上這事仗扬±鞑” “怎么了凡简?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵帜乞,是天一觀的道長筐眷。 經(jīng)常有香客問我匀谣,道長武翎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任符隙,我火速辦了婚禮霹疫,結果婚禮上丽蝎,老公的妹妹穿的比我還像新娘屠阻。我一直安慰自己,他們只是感情好臊泰,可當我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布缸逃。 她就那樣靜靜地躺著,像睡著了一般丁眼。 火紅的嫁衣襯著肌膚如雪苞七。 梳的紋絲不亂的頭發(fā)上蹂风,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天惠啄,我揣著相機與錄音撵渡,去河邊找鬼趋距。 笑死棚品,一個胖子當著我的面吹牛廊敌,可吹牛的內(nèi)容都是我干的骡澈。 我是一名探鬼主播肋殴,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼官地!你這毒婦竟也來了驱入?” 一聲冷哼從身側響起亏较,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤遵岩,失蹤者是張志新(化名)和其女友劉穎巡通,沒想到半個月后正卧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跪解,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡窘行,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年罐盔,在試婚紗的時候發(fā)現(xiàn)自己被綠了惶看。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡本今,死狀恐怖冠息,靈堂內(nèi)的尸體忽然破棺而出逛艰,到底是詐尸還是另有隱情搞旭,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站恳啥,受9級特大地震影響钝的,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沿猜,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祈坠。 院中可真熱鬧赦拘,春花似錦芬沉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椭员。三九已至车海,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隘击,已是汗流浹背侍芝。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留埋同,地道東北人州叠。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像凶赁,于是被迫代替她去往敵國和親咧栗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,499評論 2 348

推薦閱讀更多精彩內(nèi)容