RxPermissions源碼解析

lifttime1983 8年前發布 | 8K 次閱讀 RxJava 安卓開發

1.簡介

RxPermissions 是基于 RxJava 開發的用于幫助在 Android 6.0 中處理運行時權限檢測的框架。在 Android 6.0 中,系統新增了部分權限的運行時動態獲取。而不再是在以前的版本中安裝的時候授予權限。

對于運行時的權限獲取提示,國內的 Android 工程師們應該并不陌生,國內的第三方 ROM 例如 MIUI 在很早前就做了類似的功能。但是第三方 ROM 并不能提供給我們權限請求成功或失敗的接口,這就導致我們無法通過 PackageManager 提供的 checkPermission() 方法來準確的獲取到我們是否獲得該權限。只能根據具體的權限來做相應的處理。但是在 Android 6.0 中我們可以準確的獲取我們的應用是否獲取某個權限,大致方法是通過 API 23 中的 Activity 的 requestPermissions(String[] permissions, int requestCode); 方法請求權限,并在 onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) 回調方法中處理請求結果。這個方法與 onActivityResult() 類似。 RxPermissions 在這個基礎上做了封裝,使我們在處理運行時權限變得更加的簡單。

另外關于 RxJava 如果你現在還沒了解過 RxJava 可以直接翻到文章最下面去查看我總結的一些 RxJava 相關的文章,不然并不推薦直接看這篇文章。下面我們就來具體看看 RxPermissions 的使用方法以及源碼分析。

2.使用方法

1.直接獲取權限(使用Retrolambda使代碼更加簡潔,當然并不是必須使用):

// 必須在初始化階段調用,例如onCreate()方法中
RxPermissions.getInstance(this)
    .request(Manifest.permission.CAMERA)
    .subscribe(granted -> {
        if (granted) { // 在android 6.0之前會默認返回true
           // 已經獲取權限
        } else {
           // 未獲取權限
        }
    });

2.通過條件觸發獲取權限(結合RxBinding使用)

// 必須在初始化階段調用,例如onCreate()方法中
RxView.clicks(findViewById(R.id.enableCamera))
    .compose(RxPermissions.getInstance(this).ensure(Manifest.permission.CAMERA))
    .subscribe(granted -> {
        // 當R.id.enableCamera被點擊的時候觸發獲取權限
    });

3.一次請求多個權限(有兩種方式)

如果同時請求多個權限,下面這種方式會合并請求結果,即所有權限請求成功會返回 true ,若有一個權限未成功則返回 false 。

RxPermissions.getInstance(this)
    .request(Manifest.permission.CAMERA,
             Manifest.permission.READ_PHONE_STATE)
    .subscribe(granted -> {
        if (granted) {
           // 所有權限請求被同意
        } else {
           // 至少有一個權限沒同意
        }
    });

當然你可以通過 requestEach or ensureEach 來分別獲取每一個權限請求的結果。

RxPermissions.getInstance(this)
    .requestEach(Manifest.permission.CAMERA,
             Manifest.permission.READ_PHONE_STATE)
    .subscribe(permission -> { // 會發送兩個Permission對象
        if (permission.granted) {
           // `permission.name` is granted !
        }
    });

注意:由于在請求權限的過程中 app 有可能會被重啟,所以權限請求必須放在初始化的階段,比如在 Activity.onCreate/onResume , 或者

View.onFinishInflate 方法中。如果不這樣處理,那么如果 app 在請求過程中重啟的話,權限請求結果將不會發送給訂閱者即 subscriber 。

2.整體介紹

RxPermission 一共就只有三個類: Permission 是定義的權限 model 類,用來存放權限名稱以及是否獲取權限的信息。 RxPermissions 就是最主要的類了,利用 RxJava 提供了我們上面在使用方法中介紹的所有方法。還有一個 ShadowActivity 類是用來請求權限用的。下面我們就來詳細介紹 RxPermission 的實現。

注意:如果你還未了解過 RxJava 那么可以先閱讀本文最后的一系列優秀文章。如果你已經了解過 RxJava 的話,那么下面我將會介紹 RxJava 中的部分操作符在 RxPermission 中的實際運用。相信能幫助你更好的理解 RxJava 中操作符的使用。

3.源碼分析

我們依然按照我們慣用的方法來分析,通過使用方法來分析調用流程,最終理解整個項目。首先再回顧一遍使用方法(注意這里我同時請求了兩個權限),那么結果將是如果所有權限請求成功會返回 true ,若有一個權限未成功則返回 false 。代碼如下:

// 必須在初始化階段調用,例如onCreate()方法中
RxPermissions.getInstance(this)
    .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
    .subscribe(granted -> {
        if (granted) { // 在android 6.0之前會默認返回true
           // 已經獲取權限
        } else {
           // 未獲取權限
        }
    });

1.RxPermissions.getInstance(this)的實現

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;
}</code></pre> 

很明顯是維護 RxPermissions 的單例,不再多做介紹,緊接著我們來看 RxPermissions 中 request(Manifest.permission.CAMERA) 方法的實現:

2.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)的實現:

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 操作符可以將某個對象轉化為 Observable 對象,并且將其發射出去,可以是一個數字、一個字符串、數組、Iterate對象等,是 RxJava 中非常快捷的創建 Observable 對象的方法。在這里 just() 方法中雖然傳入的是 null 但是并不影響創建出的 Observable 的作用,如果有 subscriber 訂閱依然會依次調用其 onNext() 和 onCompleted() 方法。所以這里就是為了創建出一個 Observable 對象,便于后續的處理。創建完 Observable 對象之后緊接著調用了 compose() 方法:

2.compose(Transformer)操作符

compose() 操作符是針對 Observable 自身的變換,通過我們自己定義的 Transformer 對象可以將對 Observable 對象變換的操作封裝起來,實現一個簡單的 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> 對象轉換成 Observable<Boolean> 對象了。當然在 Transformer 里你也可以返回一個全新的 Observable 對象。 RxPermissions 就是這樣做的,那么回到項目中再來看 compose(ensure(permissions)); 那么 ensure(permissions); 一定是返回一個 Transformer 對象了。

3.ensure(permissions);方法的實現

我們來看看 ensure(permissions) 方法的實現:

public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
        //創建一個Transformer對象返回
        return new Observable.Transformer<Object, Boolean>() {
            // o表示當前Observable對象。
            @Override
            public Observable<Boolean> call(Observable<Object> o) {
                //request(o, permissions) 方法返回 Observable<Permission>對象
                return request(o, permissions)
                        // 將Observable<Permission>轉換為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() 方法里首先調用了 request(o, permissions) 方法,然后又進行了 buffer() 和 flatMap() 的處理,最終會返回 Observable.empty(); 、 Observable.just(false); 或 Observable.just(true); 對象。我們先來看看 request(o, permissions) 方法的實現:

4.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 Func1<Object, Observable<Permission>>() {
                    @Override
                    public Observable<Permission> call(Object o) {
                        return request_(permissions);
                    }
                });
    }

首先對 permissions 做了判斷,然后調用了 oneOf(trigger, pending(permissions)) 方法,并通過 flatMap() 操作符在 call() 方法中調用了 request_(permissions); 按照慣例我們應該去看 oneOf() 方法的實現了。但是 oneOf() 方法里其實并沒有什么實際意義,看了項目的 commit log 我覺得應該是歷史遺留問題,作者可能是想處理一些相同重復的權限請求,但是并沒有實現。所以其實這個方法完全可以這樣代替;

private Observable<Permission> request(final Observable<?> trigger,
                                           final String... permissions) {
       return request_(permissions);
    }

直接調用 request_(permissions); 即可。我測試中并沒有發現問題。目前我還沒有聯系到作者詢問這個方法的實現目的,稍后可能會提一個 issue ,如果有結果會在文章中更新。所以這里大家就完全可以理解成直接調用了 request_(permissions); 方法:

5.request_(permissions);方法的實現

@TargetApi(Build.VERSION_CODES.M)
    private Observable<Permission> request_(final String... permissions) {

        //創建出一個存放Observable<Permission>的list
        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        //存放還為請求權限的list
        List<String> unrequestedPermissions = new ArrayList<>();

        // 在請求多個權限的時候,我們為每一個請求的權限都創建一個observable對象,在最后
        // 這些observable會被合并成一個response。
        for (String permission : permissions) {
            log("Requesting permission " + permission);
            //如果是已經獲得的權限,或者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;
            }
            // 如果是已經拒絕的權限則添加
            // 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 不存在當前 permission,則添加到unrequestedPermissions中
            // 并且創建PublishSubject對象并添加到mSubjects中。
            if (subject == null) {
                unrequestedPermissions.add(permission);
                subject = PublishSubject.create();
                mSubjects.put(permission, subject);
            }
            //并且添加到list中
            list.add(subject);
        }

        //如果有未請求的權限
        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);
    }

代碼如上,注釋非常清晰,整體上就是將已經允許的權限和已經拒絕過的權限添加到 list 中,并且將還未請求的權限分別添加到 mSubjects 與 list 中。然后調用 startShadowActivity(); 方法。最后通過 Observable.concat(Observable.from(list)); 返回。這里主要包含 PublishSubject 與 concat() 操作符的知識:

6.PublishSubject對象

從 PublishSubject 的 文檔 中,可以看出是繼承自 Subject , Subject 是既可以充當 Observer 又能充當 Observable 的。從文檔中的 Example 中可以看到訂閱 PublishSubject 的 observer 只會接收到訂閱之后 PublishSubject 發送的數據,但是本項目中并沒有體現出此特性,主要是利用 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 結合成一個 Observable 并發射數據,并且嚴格按照先后順序發射數據,前一個 Observable 的數據沒有發射完,是不能發射后面 Observable 的數據的。所以在本項目中 Concat() 是為了保證請求的權限按順序返回。接下來我們看看 ShadowActivity 的實現:

8.ShadowActivity的實現

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

很簡單其實就是按照系統提供給我們的方法進行權限請求,最后回調 RxPermissions 的 onRequestPermissionsResult() 方法:

9.onRequestPermissionsResult()方法的實現

void onRequestPermissionsResult(int requestCode,
                                    String permissions[], int[] grantResults) {
        for (int i = 0, size = permissions.length; i < size; i++) {
            log("onRequestPermissionsResult  " + permissions[i]);
            // 取出對應的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]);
            //獲取結果
            boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
            //調用onNext()方法發送結果
            subject.onNext(new Permission(permissions[i], granted));
            //調用onCompleted()方法。
            subject.onCompleted();
        }
    }

簡單的來說就是拿到結果并發送結果。所以就又回到了最初的 ensure(permissions); 方法中的 request(o, permissions) 之后,代碼如下:

request(o, permissions)
        // 將Observable<Permission>轉換為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<Permission> 對象,請求了幾個權限就會發送幾次,但是這里用了一個 buffer() 操作符,關于 buffer() 操作符:

10.buffer()操作符

buffer 英文是緩沖區的意思。所以Buffer操作符所要做的事情就是將數據按照規定的大小做一下緩存,然后將緩存的數據作為一個集合發射出去。詳細可以看 這里 ,所以在本項目中就是講這些 Observable<Permission> 轉換成 Observable<List<Permission>> 對象,緊接著又使用了 flatMap() 操作符然后返回了我們最終的結果。以上就是整個的調用流程了,如果有不清楚的建議可以多多的調試 RxPermission 的代碼以及查閱各種資料幫助理解。

11. requestEach()、ensureEach()、ensure()的實現

以上我們分析了 request() 方法的實現,看似好像還剩下上面三個方法沒有分析。其實仔細看的同學應該已經看明白了。上面三個方法其實都是差不多的。如果你看懂了 request() 方法的實現,那么這三個方法你一定能看懂,有興趣的同學可以自行去源碼里研究。

4.個人評價

其實 Android 6.0 的權限處理我自己在項目中都沒有使用過,因為拿目前國內市場來說,首先 Android 6.0 的手機占有量非常少。再者我們可以使用很簡單的方法將 targetSdkVersion 設置為 22 來兼容 6.0 的權限處理。所以目前項目中應該很少需要使用 RxPermissions 這個項目。但是這個項目作為 RxJava 的學習資料是相當的好。從中我們可以學到大量的 RxJava 相關的使用知識。如果你現在在學習 RxJava ,強烈推薦這個項目。

 

來自:http://www.jianshu.com/p/c8a30200e6b2

 

 本文由用戶 lifttime1983 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!