美文网首页6.0首页投稿(暂停使用,暂停投稿)Android开发
项目需求讨论 - 动态权限申请分析及相关第三方库源码分析

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

作者: 青蛙要fly | 来源:发表于2017-09-28 17:26 被阅读217次

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

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

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

    前言

    1. Android 6.0以下:
      都是直接在AndroidManifest.xml中直接填入我们想要的权限即可。
      比如获取手机状态的权限,我们就直接在这个xml文件中填入:
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
    1. 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:最后提到一个大坑,那就是不同系统的国产手机,申请权限的时候,有时候提示成功了。可是还是没有获取到,明明拒绝了,可能还是获取成功。毕竟国内的手机厂家太多。我也就这边提一下。不知道大家遇到过没有。

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/tnrvextx.html