项目需求讨论 - 动态权限申请分析及相关第三方库源码分析

hi,又到了我们具体开发时候遇到的项目需求讨论了。

在具体项目开发中,关于Android的动态申请权限的功能,我想大家都见怪不怪了。很多人开发的app中也都使用过这块需求。

PS:本文我写的比较啰嗦,特别是对RxPermission和easyPermission的源码分析,因为我写的比较细(又或者是啰嗦及累赘),所以造成篇幅会很大。可能很多人都不会有耐心看完。

前言

  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:题外话

  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 动态申请权限:

这里我会分三大块来讲:

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

原生API支持:

我们先从简单的说起,在Android6.0出来后,在各种第三方权限库还没出来的时候,大家普遍使用的是谷歌原生的申请权限的流程代码:

1. 检查权限(check permission):

checkSelfPermission(String permission):23版本api添加,用来检测自己是否授予了指定permission 。

Context.checkPermission (String permission, int pid, int uid):用来检测指定uid和pid的进程中是否授予了指定的permission。

Context.checkCallingOrSelfPermission (String permission):用来检测自己或者调用进程中是否授予了指定permission。

Context.checkCallingPermission (String permission):检查正在处理的调用者进程是否授予指定permission 权限,如果调用者是自己那么返回 。

除了上面的大家可能用的比较多的是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:最后提到一个大坑,那就是不同系统的国产手机,申请权限的时候,有时候提示成功了。可是还是没有获取到,明明拒绝了,可能还是获取成功。毕竟国内的手机厂家太多。我也就这边提一下。不知道大家遇到过没有。

最后的最后,写完了。。如果哪里写错了。希望大家轻点喷。哈哈。。可以留言指出。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android知识点总结

P1-Android基于MVP实现号码归属地查询

12020
来自专栏Android先生

RxJava2 实战知识梳理(5) - 简单及进阶的轮询操作

今天,我们介绍一种新的场景,轮询操作。也就是说,我们会尝试间隔一段时间就向服务器发起一次请求,在使用RxJava之前,该需求的实现一般有两种方式:

24920
来自专栏三流程序员的挣扎

RxJava Single Completable Maybe 使用和源码阅读

Single 用于只发射一次数据就结束了,所以无需通过 onComplete 通知观察者,要么 onSuccess 要么 onError。

23720
来自专栏JackieZheng

探秘Tomcat——启动篇

tomcat作为一款web服务器本身很复杂,代码量也很大,但是模块化很强,最核心的模块还是连接器Connector和容器Container。具体请看下图: ? ...

50170
来自专栏芋道源码1024

熔断器 Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑

本文主要基于 Hystrix 1.5.X 版本 1. 概述 2. #applyHystrixSemantics(...) 3. TryableSemaphor...

53880
来自专栏向治洪

Volley解析之表单提交篇

要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式。  数据包: Connection: ...

23450
来自专栏菩提树下的杨过

rpc框架之 thrift连接池实现

接前一篇rpc框架之HA/负载均衡构架设计 继续,写了一个简单的thrift 连接池: 先做点准备工作: package yjmyzz; public cla...

67380
来自专栏QQ空间开发团队的专栏

RxJava && Agera 从源码简要分析基本调用流程(1)

相信很多做Android或是Java研发的同学对RxJava应该都早有耳闻了,尤其是在Android开发的圈子里,RxJava渐渐开始广为流行。同样有很多同学已...

8.6K10
来自专栏学海无涯

Android开发之自动填充短信验证码

笔者发现在很多应用中,都有自动获取验证码的功能:点击获取验证码按钮,收到短信,当前应用不需要退出程序就可以获取到短信中的验证码,并自动填充。觉得这种用户体验很赞...

51880
来自专栏刘晓杰

retrofit的使用

367140

扫码关注云+社区

领取腾讯云代金券