項目需求討論 - 動態權限申請分析及相關第三方庫源碼分析

hi,又到了咱們具體開發時候遇到的項目需求討論了。java

在具體項目開發中,關於Android的動態申請權限的功能,我想你們都見怪不怪了。不少人開發的app中也都使用過這塊需求。android

PS:本文我寫的比較囉嗦,特別是對RxPermission和easyPermission的源碼分析,由於我寫的比較細(又或者是囉嗦及累贅),因此形成篇幅會很大。可能不少人都不會有耐心看完。git

前言

  1. Android 6.0如下:
    都是直接在AndroidManifest.xml中直接填入咱們想要的權限便可。
    好比獲取手機狀態的權限,咱們就直接在這個xml文件中填入:
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />複製代碼
  2. Android 6.0以上:
    對於普通權限(Normal Permission)仍是繼續直接在AndroidManifest.xml中寫入便可,能夠得到其權限
    對於一些重要的權限(Dangerous Permission)咱們須要動態去申請的時候,咱們須要動態去獲取。好比:
    身體傳感器
    日曆
    攝像頭
    通信錄
    地理位置
    麥克風
    電話
    短信
    存儲空間複製代碼
    主要是上述幾大塊相關的權限,須要動態去獲取,具體見下面列表:

PS:題外話github

  1. targetSDKVersion < 23 & API(手機系統) < 6.0:安裝時默認得到權限,且用戶沒法在安裝App以後取消權限。
  2. targetSDKVersion >= 23 & API(手機系統) < 6.0:安裝時默認得到權限,且用戶沒法在安裝App以後取消權限。
  3. targetSDKVersion < 23 & API(手機系統) >= 6.0:安裝時默認得到權限,可是用戶能夠在安裝App完成後動態取消受權(取消時手機會彈出提醒,告訴用戶這個是爲舊版手機打造的應用,讓用戶謹慎操做)。
  4. targetSDKVersion >= 23 & API(手機系統) >= 6.0:安裝時不會得到權限,能夠在運行時向用戶申請權限。用戶受權之後仍然能夠在設置界面中取消受權。

Android 動態申請權限:

這裏我會分三大塊來說:api

  1. 原生API支持
  2. easyPermission
  3. RxPermissions (Rxjava2)

原生API支持:

咱們先從簡單的提及,在Android6.0出來後,在各類第三方權限庫還沒出來的時候,你們廣泛使用的是谷歌原生的申請權限的流程代碼:緩存

1. 檢查權限(check permission):

checkSelfPermission(String permission):23版本api添加,用來檢測本身是否授予了指定permission 。bash

Context.checkPermission (String permission, int pid, int uid):用來檢測指定uid和pid的進程中是否授予了指定的permission。併發

Context.checkCallingOrSelfPermission (String permission):用來檢測本身或者調用進程中是否授予了指定permission。 app

Context.checkCallingPermission (String permission):檢查正在處理的調用者進程是否授予指定permission 權限,若是調用者是本身那麼返回 。 less

除了上面的你們可能用的比較多的是ContextCompat.checkSelfPermission(),但其實咱們能夠跟進去看:

public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }
    return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}複製代碼

最終調用的仍是根據
Context.checkPermission (String permission, int pid, int uid)(用來檢測指定uid和pid的進程中是否授予了指定的permission)來進行檢查。

固然還有其餘的攜帶Uri的permission檢查,不過我沒有試驗過,用過的小夥伴留個言,看是在什麼狀況下使用及怎麼使用。
相似:

Context.checkCallingOrSelfUriPermission (Uri uri, int modeFlags) 
Context.checkCallingUriPermission (Uri uri, int modeFlags); 
Context.checkUriPermission (Uri uri, int pid, int uid, int modeFlags);
Context.checkUriPermission (Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags);複製代碼

咱們調用相關方法後,若是返回的int值與PackageManager.PERMISSION_GRANTED值相等,那麼表示已經受權了,若是爲PackageManager.PERMISSION_DENIED,則說明申請權限被拒絕了。

2.請求權限:

int requestCode = 1;
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},requestCode);複製代碼

3.回調事件處理:

/**
 * 參數1:requestCode-->是requestPermissions()方法傳遞過來的請求碼。
 * 參數2:permissions-->是requestPermissions()方法傳遞過來的須要申請權限
 * 參數3:grantResults-->是申請權限後,系統返回的結果,PackageManager.PERMISSION_GRANTED表示受權成功,PackageManager.PERMISSION_DENIED表示受權失敗。
 * grantResults和permissions是一一對應的
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}複製代碼

4.請求權限提示:

android6.0(API23)及以上,提供了一個方法:shouldshowrequestpermissionrationale(),若是應用程序請求此權限,而且用戶拒絕了請求,則此方法返回true。

ps:用戶在過去拒絕了權限請求,對話框中選擇了「再也不詢問」選項,該方法返回false。若是設置中禁止應用程序具備該權限,該方法還將返回false。

因此咱們能夠當用戶拒絕了這個權限的時候,咱們能夠用這個方法判斷,而後能夠彈出一個彈框,而且寫上咱們的提示內容,好比咱們能夠彈出一個彈框,上面寫着「若是要使用咱們的XXX功能,必定要開啓XXX權限哦!!!!」,而後同時咱們可讓點擊彈框的「確認」按鈕後跳到「設置」應用,讓用戶手動去打開權限。

咱們也能夠在Activity中覆寫該方法:

@Override
public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
  return super.shouldShowRequestPermissionRationale(permission);
}複製代碼

easyPermission:

前面咱們已經講了如何用沒有使用封裝的庫直接用原生API來進行權限申請。接下去咱們要介紹easyPermisson;
相關github連接:easyPermisson

這個庫是谷歌推出的,因此我也使用過這個。怎麼使用這個庫我就很少說了。咱們直接來看相關的源碼分析。

咱們直接看他官方給的Demo:

1. 判斷是否有權限:

EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA);
好比查詢是否有相機權限,咱們繼續看具體的hasPermissions方法的內容:

public static boolean hasPermissions(Context context, @NonNull String... perms) {
    // Always return true for SDK < M, let the system deal with the permissions
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        Log.w(TAG, "hasPermissions: API version < M, returning true by default");

        // DANGER ZONE!!! Changing this will break the library.
        return true;
    }

    // Null context may be passed if we have detected Low API (less than M) so getting
    // to this point with a null context should not be possible.
    if (context == null) {
        throw new IllegalArgumentException("Can't check permissions for null context");
    }

    for (String perm : perms) {
        if (ContextCompat.checkSelfPermission(context, perm)
                != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }

    return true;
}複製代碼

咱們能夠看到,先會判斷SDK是不是小於android 6.0,若是是那麼直接返回true,由於6.0如下不須要動態申請;而後咱們發現它這裏接下去檢查是否有權限用的就是咱們上面提到過的谷歌API中的ContextCompat.checkSelfPermission()方法,這個方法上面已經介紹過了。這裏也很少說了。能夠文章往上拉一下看看。

2. 申請權限:

前提,咱們假設是在Activity中去申請相關權限(PS:若是是fragment的話,一些方法的調用不一樣,可是原理是差很少的。)

private static final int RC_CAMERA_PERM = 123;

EasyPermissions.requestPermissions(
        this,
        getString(R.string.rationale_camera),
        RC_CAMERA_PERM,
        Manifest.permission.CAMERA);複製代碼

咱們一步步分析下去:

/**
 * Request permissions from an Activity with standard OK/Cancel buttons.
 *
 * @see #requestPermissions(Activity, String, int, int, int, String...)
 */
public static void requestPermissions(
        @NonNull Activity host, @NonNull String rationale,
        int requestCode, @NonNull String... perms) {

    requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
            requestCode, perms);
}複製代碼

咱們看見調用了另一個requestPermissions方法,繼續看下去:

/**
 * Request permissions from a Support Fragment.
 *
 * @see #requestPermissions(Activity, String, int, int, int, String...)
 */
public static void requestPermissions(
        @NonNull Fragment host, @NonNull String rationale,
        @StringRes int positiveButton, @StringRes int negativeButton,
        int requestCode, @NonNull String... perms) {

    requestPermissions(PermissionHelper.newInstance(host), rationale,
            positiveButton, negativeButton,
            requestCode, perms);
}複製代碼

在這裏,咱們看到它經過PermissionHelper.newInstance(host)生成了一個PermissionHelper對象。咱們繼續查看接下去調用的requestPermissions方法:

private static void requestPermissions(
    @NonNull PermissionHelper helper, @NonNull String rationale,
    @StringRes int positiveButton, @StringRes int negativeButton,
    int requestCode, @NonNull String... perms) {

    // Check for permissions before dispatching the request
    if (hasPermissions(helper.getContext(), perms)) {
        notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms);
        return;
    }

    // Request permissions
    helper.requestPermissions(rationale, positiveButton,
            negativeButton, requestCode, perms);
}複製代碼

咱們能夠看到這裏面用到了咱們上面新生成的PermissionHelper對象的相關方法,在申請權限前,又調用了一次hasPermissions方法來判斷下是否已經有該權限,若是有就直接return出這個方法。而後沒有該權限,就調用helper的方法

helper.requestPermissions(rationale, positiveButton,
        negativeButton, requestCode, perms);複製代碼

因此咱們知道了接下去確定是調用了PermissionHelper裏面相關的方法,那咱們上面建立該對象的時候是用

PermissionHelper.newInstance(host)複製代碼

因此咱們看下它具體生成對象的方法:

@NonNull
public static PermissionHelper newInstance(Activity host) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return new LowApiPermissionsHelper(host);
    }

    if (host instanceof AppCompatActivity) {
        return new AppCompatActivityPermissionHelper((AppCompatActivity) host);
    } else {
        return new ActivityPermissionHelper(host);
    }
}複製代碼

咱們發現當小於Android 6.0的時候,返回的是LowApiPermissionsHelper,而這個類裏面的相關方法,都是拋出異常,好比說:

@Override
public void showRequestPermissionRationale(
        @NonNull String rationale,int positiveButton, 
        int negativeButton,int requestCode,
        @NonNull String... perms) {

        throw new IllegalStateException("Should never be requesting permissions on API < 23!");

    }複製代碼

而咱們在Android 6.0以上獲取到的就是AppCompatActivityPermissionHelper對象或者ActivityPermissionHelper對象。
咱們這裏由於Activity繼承AppCompatActivity,因此咱們這裏生成的是是AppCompatActivityPermissionHelper對象。
咱們查看到調用到的PermissionHelper中的requestPermission方法:

public void requestPermissions(@NonNull String rationale,
                       @StringRes int positiveButton,
                       @StringRes int negativeButton,
                       int requestCode,
                       @NonNull String... perms) {

    if (shouldShowRationale(perms)) {
        showRequestPermissionRationale(
                rationale, positiveButton, negativeButton, requestCode, perms);
    } else {
        directRequestPermissions(requestCode, perms);
    }
}複製代碼

咱們能夠看到在else的代碼塊中,最後調用了AppCompatActivityPermissionHelperdirectRequestPermissions方法(if中代碼塊的內容放在4.請求權限提示中具體講解):

@Override
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
    ActivityCompat.requestPermissions(getHost(), perms, requestCode);
}複製代碼

咱們就發現了,最終它的申請權限的核心代碼仍是調用了咱們上面介紹過的谷歌的API的ActivityCompat.requestPermissions()方法。

3.回調事件處理:

@Override
public void onRequestPermissionsResult(int requestCode,
                           @NonNull String[] permissions,
                           @NonNull int[] grantResults) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    // EasyPermissions handles the request result.
    EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }複製代碼

咱們能夠看到在onRequestPermissionsResult回調方法中,調用

EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);複製代碼

ps:這裏介紹下第四個參數:

an array of objects that have a method annotated with {@link AfterPermissionGranted} or implement {@link PermissionCallbacks}複製代碼

須要有註解AfterPermissionGranted的方法,或者是實現了PermissionCallbacks接口。咱們這邊是直接在Activity中實現了PermissionCallbacks接口,因此只須要傳入this便可。

public interface PermissionCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback {
    void onPermissionsGranted(int requestCode, List<String> perms);
    void onPermissionsDenied(int requestCode, List<String> perms);
}複製代碼

咱們具體的看EasyPermissions.onRequestPermissionsResult這個方法的代碼:

public static void onRequestPermissionsResult(int requestCode,
                                  @NonNull String[] permissions,
                                  @NonNull int[] grantResults,
                                  @NonNull Object... receivers){

    // Make a collection of granted and denied permissions from the request.
    List<String> granted = new ArrayList<>();
    List<String> denied = new ArrayList<>();
    for (int i = 0; i < permissions.length; i++) {
        String perm = permissions[i];
        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
            granted.add(perm);
        } else {
            denied.add(perm);
        }
    }

    // iterate through all receivers
    for (Object object : receivers) {
        // Report granted permissions, if any.
        if (!granted.isEmpty()) {
            if (object instanceof PermissionCallbacks) {
                ((PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);
            }
        }

        // Report denied permissions, if any.
        if (!denied.isEmpty()) {
            if (object instanceof PermissionCallbacks) {
                ((PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);
            }
        }

        // If 100% successful, call annotated methods
        if (!granted.isEmpty() && denied.isEmpty()) {
            runAnnotatedMethods(object, requestCode);
        }
    }
}複製代碼

咱們能夠看到新建了二個ArrayList(granted 和 denied),分別存放經過的權限及拒絕的權限,當granted裏面的個數不爲0,則調用PermissionCallbacksonPermissionsGranted(requestCode, granted);若是denied不爲空的時候,則調用PermissionCallbacksonPermissionsDenied(requestCode, granted);而後若是granted不爲空,同時denied爲空,說明所申請的權限所有被贊成了。則調用被@AfterPermissionGranted修飾的方法。

4.請求權限提示:

咱們在用easyPermission申請權限的時候:

EasyPermissions.requestPermissions(
            this,
            getString(R.string.rationale_location_contacts),
            RC_LOCATION_CONTACTS_PERM,
            LOCATION_AND_CONTACTS);複製代碼

咱們能夠看到咱們在第二個參數傳了一個字符串。這個字符串就是當咱們的權限被拒絕後,用來提示用戶時候顯示的文字。咱們能夠仔細看下原理,咱們再回到PermissionrequestPermission方法中:

public void requestPermissions(@NonNull String rationale,
                       @StringRes int positiveButton,
                       @StringRes int negativeButton,
                       int requestCode,
                       @NonNull String... perms) {
    if (shouldShowRationale(perms)) {
        showRequestPermissionRationale(
                rationale, positiveButton, negativeButton, requestCode, perms);
    } else {
        directRequestPermissions(requestCode, perms);
    }
}複製代碼

咱們能夠看到,若是shouldShowRationale方法返回false,纔會去調用咱們的申請權限的方法,咱們看下shouldShowRationale

public boolean shouldShowRationale(@NonNull String... perms) {
    for (String perm : perms) {
        if (shouldShowRequestPermissionRationale(perm)) {
            return true;
        }
    }
    return false;
}複製代碼

shouldShowRequestPermissionRationale調用了

@Override
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
    return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
}複製代碼

咱們發現最終調用了ActivityCompat.shouldShowRequestPermissionRationale,因此只要有一個權限被拒絕了後,就會返回true,而後調用:

showRequestPermissionRationale(
    rationale, positiveButton, negativeButton, requestCode, perms);複製代碼

咱們具體看代碼,是在BaseSupportPermissionsHelper.java中:

public abstract class BaseSupportPermissionsHelper<T> extends PermissionHelper<T> {

    public BaseSupportPermissionsHelper(@NonNull T host) {
        super(host);
    }

    public abstract FragmentManager getSupportFragmentManager();

    @Override
    public void showRequestPermissionRationale(@NonNull String rationale,
                           int positiveButton,
                           int negativeButton,
                           int requestCode,
                           @NonNull String... perms) {

        RationaleDialogFragmentCompat
                .newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
                .show(getSupportFragmentManager(), RationaleDialogFragmentCompat.TAG);
    }
}複製代碼

在這裏最後彈出一個彈框,彈框的內容就是咱們傳入的字符串,而後「肯定」按鈕設置了點擊事件:

@Override
public void onClick(DialogInterface dialog, int which) {
    if (which == Dialog.BUTTON_POSITIVE) {
        if (mHost instanceof Fragment) {
            PermissionHelper.newInstance((Fragment) mHost).directRequestPermissions(
                    mConfig.requestCode, mConfig.permissions);
        } else if (mHost instanceof android.app.Fragment) {
            PermissionHelper.newInstance((android.app.Fragment) mHost).directRequestPermissions(
                    mConfig.requestCode, mConfig.permissions);
        } else if (mHost instanceof Activity) {
            PermissionHelper.newInstance((Activity) mHost).directRequestPermissions(
                    mConfig.requestCode, mConfig.permissions);
        } else {
            throw new RuntimeException("Host must be an Activity or Fragment!");
        }
    } else {
        notifyPermissionDenied();
    }
}複製代碼

當用戶按了確認按鈕後,從新去進行權限的申請方法:

PermissionHelper.newInstance((Activity) mHost).directRequestPermissions(
       mConfig.requestCode, mConfig.permissions);複製代碼

RxPermissions

RxPermissions是我第二個使用的權限申請庫,由於項目中使用了Rxjava2,因此裏面也就使用了基於Rxjava2的RxPermissions。咱們也仍是同樣來看RxPermission是怎麼封裝的。

咱們直接看申請權限的完整代碼:

RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.request(Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.READ_PHONE_STATE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE
    ).subscribe(new Observer<Boolean>() {
        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onNext(Boolean aBoolean) {
            if (aBoolean) {
                BaseToast.info("獲取相應應用權限成功");
            } else {
                BaseToast.error("獲取相應應用權限失敗");
            }
        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onComplete() {

        }
    });複製代碼

PS:這裏我使用request 來分析,request是申請多個權限時候,好比咱們申請三個,就要這三個都被用戶贊成後,纔會返回true,可是咱們也可使用requestEach來分別對每一個權限的申請結果來進行處理

第一步:

RxPermissions rxPermissions = new RxPermissions(this);
咱們能夠看到咱們實例化了RxPermissions的對象了。

第二步:

rxPermissions.request(Manifest.permission.ACCESS_COARSE_LOCATION,
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_PHONE_STATE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE
)複製代碼

咱們跟進去看request裏面作了什麼處理:

public Observable<Boolean> request(final String... permissions) {
    return Observable.just(TRIGGER).compose(ensure(permissions));
}複製代碼

咱們能夠看到這個方法最後返回了一個Observable<Boolean>對象,而這個Boolean值就是咱們最後是否是把全部申請的權限都贊成的結果值,若是都贊成則返回true,不然返回false;咱們能夠看到:

static final Object TRIGGER = new Object();
Observable.just(TRIGGER);複製代碼

建立一個Observable用來發射信息,信息內容是一個Object對象,這裏只是單純爲了建立一個Observable而已,因此發射什麼內容無所謂。Rxjava1中的是直接Observable.just(null),可是在Rxjava2中這麼寫是會報錯的,因此這裏直接發射了一個Object對象。

Observable.just(TRIGGER).compose(ensure(permissions));複製代碼

咱們能夠看到把咱們本來發射對象Object的Observable變爲了Observable<Boolean>。因此就算我先不講compose操做符的做用,你們也都能猜到是用來轉換Observable。


咱們能夠看到API中Compose的介紹:經過一個特定的Transformer函數來轉換Observable。

因此咱們也就能夠猜到咱們代碼中的ensure(permissions)是一個Transformer。

public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
    return new ObservableTransformer<T, Boolean>() {
        @Override
        public ObservableSource<Boolean> apply(Observable<T> o) {
            return 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) throws Exception {
                            if (permissions.isEmpty()) {
                                // Occurs during orientation change, when the subject receives onComplete.
                                // In that case we do not 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);
                        }
                    });
        }
    };
}複製代碼

咱們能夠看到返回了一個ObservableTransformer對象,用來轉換Observable的。咱們繼續看裏面的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 Function<Object, Observable<Permission>>() {
                @Override
                public Observable<Permission> apply(Object o) throws Exception {
                    return requestImplementation(permissions);
                }
            });
}複製代碼

能夠看到若是咱們申請的權限爲空或者個數爲0,則拋出異常,不然返回一個Observable<Permission>

咱們繼續看oneOf(trigger, pending(permissions))方法:

private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
    if (trigger == null) {
        return Observable.just(TRIGGER);
    }
    return Observable.merge(trigger, pending);
}複製代碼

其實這邊的oneOf方法是判斷若是觸發的Observable爲空,則直接返回一個發射Object的Observable,否則就合併觸發的Observablepending這個Observablepending這個Observable是由pending(permissions)生成的。

pending方法:

private Observable<?> pending(final String... permissions) {
    for (String p : permissions) {
        if (!mRxPermissionsFragment.containsByPermission(p)) {
            return Observable.empty();
        }
    }
    return Observable.just(TRIGGER);
}複製代碼

這裏咱們發現把咱們傳入的權限字符串,拿到了mRxPermissionsFragment去判斷:

public PublishSubject<Permission> setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject<Permission> subject) {
    return mSubjects.put(permission, subject);
}複製代碼

mRxPermissionsFragment中維護了一個HashMap集,裏面維護了一個key爲權限字符串,value爲每一個權限相對於的Observable的鍵值對。這樣若是咱們重複申請某個權限的時候,咱們直接返回了一個建立一個不發射任何數據可是正常終止的ObservableObservable.empty(),否則就返回一個發送Object的Observable
因此這裏oneOf方法最終的結果是:二個Observable.just(TRIGGER)合併發送,或者一個Observable.just(TEIGGER)與一個Observable.empty()合併,也就是發送二次Object,或者發送一次Object(由於empty不發送)。

由於後面在對某個權限申請作贊成或者拒絕的時候,就會把這個權限的key-value從mRxPermissionsFragment的HashMap中移除,因此這邊得到到的一直就是Observable.just(TEIGGER)與一個Observable.empty()合併。

oneOf咱們分析過了,好了咱們回頭再來看:

oneOf(trigger, pending(permissions))
    .flatMap(new Function<Object, Observable<Permission>>() {
        @Override
        public Observable<Permission> apply(Object o) throws Exception {
            return requestImplementation(permissions);
        }
    });複製代碼

咱們知道了oneOf返回的是一個Observable,因此咱們看到了代碼對Observable進行了flatMap操做,把咱們的Observable變成了新的Observable來發送。具體的是requestImplementation(permissions);

PS:因此我以爲這裏的oneOf的功能有點問題,由於前面這樣各類判斷去獲取一個Observable,到後面仍是會被flatmap給替換掉,因此這裏我以爲不用OneOf函數去獲取而後再調用flatMap,而是直接就用requestImplementation(permissions)這個Observable我以爲就能夠了。

咱們具體來看下requestImplementation方法的實現:

@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.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.getSubjectByPermission(permission);
        // Create a new subject if not exists
        if (subject == null) {
            unrequestedPermissions.add(permission);
            subject = PublishSubject.create();
            mRxPermissionsFragment.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));
}複製代碼

咱們來分析上面這段核心代碼:

1. 判斷是否有權限:

其中Rxjava的受權與否的判斷代碼詳情:

public boolean isGranted(String permission) {
    return !isMarshmallow() || mRxPermissionsFragment.isGranted(permission);
}

@SuppressWarnings("WeakerAccess")
public boolean isRevoked(String permission) {
    return isMarshmallow() && mRxPermissionsFragment.isRevoked(permission);
}

boolean isMarshmallow() {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}複製代碼

mRxPermissionsFragment.isGranted(permission)mRxPermissionsFragment.isRevoked(permission)的代碼:

@TargetApi(Build.VERSION_CODES.M)
boolean isGranted(String permission) {
    return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}

@TargetApi(Build.VERSION_CODES.M)
boolean isRevoked(String permission) {
    return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
}複製代碼

因此判斷權限用的原生的API:getActivity().checkSelfPermission(permission),這裏和easyPermission沒什麼不一樣;可是在判斷權限處於拒絕狀態就不同了,
easyPermission用的是

getActivity().checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED;複製代碼

而RxPermission用的是getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName())

2.生成相應的不一樣權限的Observable:

咱們如今已經根據是否受權,把相關的權限進行區別,而後分別生成 放到了二個ArrayList中:

List<Observable<Permission>> list = new ArrayList<>(permissions.length);
List<String> unrequestedPermissions = new ArrayList<>();

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.getSubjectByPermission(permission);
// Create a new subject if not exists
if (subject == null) {
    unrequestedPermissions.add(permission);
    subject = PublishSubject.create();
    mRxPermissionsFragment.setSubjectForPermission(permission, subject);
}

list.add(subject);複製代碼

咱們能夠看到ArrayList裏面添加了發射內容爲Permission對象的Observable。而這個Permission中的第二個參數所對應的屬性就是用來標記是否已經處於贊成狀態。而後若是有權限還處於帶詢問狀態(既沒有贊成有沒有拒絕),則新建一個Observable,而且加入到了咱們mRxPermissionsFragment中提過的HashMap中,以便後面能夠重複使用。
最後咱們的List<Observable<Permission>> list = new ArrayList<>(permissions.length);裏面,放了已經標記了經過的PermissionObservable,被拒絕了的PermissionObservable,還有待詢問的的新建的Observable。這時候咱們若是真的還有待詢問的Observable,則調用requestPermissionsFromFragment方法去申請權限:

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

也就是最後調用了RxPermissionsFragment的申請權限的功能:
@TargetApi(Build.VERSION_CODES.M)
void requestPermissions(@NonNull String[] permissions) {
    requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
}複製代碼

因此這一塊咱們總結一下,就是遍歷咱們傳入的申請的權限字符串,而後去判斷:

  1. 若是這個申請的權限前面已經贊成過了。就直接標記一個Permission對象,包含了申請權限的name值,是否贊成的Boolean值,而且爲true。
  2. 若是這個申請權限已經被拒絕了,就直接標記一個Permission對象,包含了申請權限的name值,是否贊成的Boolean值,而且爲false。
  3. 若是直接這個申請的權限是詢問狀態,新建一個Observable,而且會根據申請權限的name爲key保存到mRxPermissionsFragment中的HashMap中,爲何要存進去呢,由於這時候要 調用Fragment的requestPermissions方法,這時候手機就會出現申請權限的申請框。若是用戶選擇了經過或者拒絕,這時候咱們就要把這個Observable發送相應的贊成的Permission對象或者拒絕的Permission對象。

而後返回了:Observable.concat(Observable.fromIterable(list));

PS:Observable.fromIterable(Iterable<? extends T> source)
此方法接收一個繼承自Iterable接口的參數,簡單的說就是java中的集合類。所以你能夠傳入一個list集合等等。

最後經過Observable.concatObservable.fromIerable生成的多數據原合併在一塊兒變爲一個Observable

這時候咱們就又回到了最剛開始的ensure方法:

public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
    return new ObservableTransformer<T, Boolean>() {
        @Override
        public ObservableSource<Boolean> apply(Observable<T> o) {
            return 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) throws Exception {
                            if (permissions.isEmpty()) {
                                // Occurs during orientation change, when the subject receives onComplete.
                                // In that case we do not 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);
                        }
                    });
        }
    };
}複製代碼

這時候咱們的request(o, permissions)已經瞭解完了。按照原來的設置是會發射一個帶有不一樣結果的Permission對象的Observable;咱們能夠看到了這裏用到了buffer操做符,buffer英文是緩衝區的意思。因此Buffer操做符所要作的事情就是將數據按照規定的大小作一下緩存,而後將緩存的數據做爲一個集合發射出去。因此在咱們下面的flatMap中的new Function的第一個參數是一個集合List<Permission>。而後就是遍歷集合,看裏面的Permissiongranted屬性是true仍是false,只要有一個false,就返回Observable.just(false),不然就Observable.just(true)

而後咱們最終在Activity的代碼就等效於(假設最後都贊成申請的權限):

Observable.just(true)
        .subscribe(new Observer<Boolean>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(Boolean aBoolean) {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
    });複製代碼

題外話:
關於rxPermissions.requestEach()只是它最後沒有用bufferflatMap拼在一塊兒去判斷而後是否發送Observable.just(true)或者是Observable.just(false)發送,而是把Permission的結果一個個發送出來。因此接受到的是Permission對象,因此咱們能夠針對Permission對象一個個去處理結果:

rxPermissions.requestEach(Manifest.permission.CAMERA,
    Manifest.permission.READ_PHONE_STATE)
    .subscribe(new Observer<Permission>() {
        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onNext(Permission permission) {

        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onComplete() {

        }
});複製代碼


PS:最後提到一個大坑,那就是不一樣系統的國產手機,申請權限的時候,有時候提示成功了。但是仍是沒有獲取到,明明拒絕了,可能仍是獲取成功。畢竟國內的手機廠家太多。我也就這邊提一下。不知道你們遇到過沒有。

最後的最後,寫完了。。若是哪裏寫錯了。但願你們輕點噴。哈哈。。能夠留言指出。

相關文章
相關標籤/搜索