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

作者:分分快三计划

  在IT界已经混了5年了,5年中浏览了不少的网站,在上面查询自己想要的东西,解决工作中遇到的问题,心里总想有天自己能够有自己的博客,能给分享一些自己在生活中、工作中遇到的问题,让其他有类似经历的朋友能够少走弯路,今天终于鼓起勇气在博客园写下第一篇随笔。其他不做过多的介绍,下面将介绍今天在工作中遇到的一些问题。

补充

也不知道该补充些啥,那么简单的介绍几个知识点吧(可能有点乱,凑合着看吧)

  1. Observablet.timer(long delay, TimeUnit unit)可以定时发送事件
  2. Filter操作符可以在数据数据发送过程中过滤掉一些数据
  3. 一般来说,Observable不会抛异常。它会调用 onError 终止Observable序列,以此通知所有的观察者发生了一个不可恢复的错误. 但是,也存在一些异常.例如:如果onError调用失败了,Observable不会尝试再次调用onError去通知观察者,它会抛出RuntimeException,OnErrorFailedException或者OnErrorNotImplementedException.
    4.暂时就这些了,以后想到的话我还会继续补充

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

此外还可用使用Observable.zip/merge也可以解决这个问题,zip与forkJoin的区别在于zip是串行,forkJoin是并行。其次是zip能够取得观察对象中的每一个值,而forkJoin只能取得对象的最后一个值作为返回值。zip的具体使用方式跟上述的forkJoin基本一致,此处就不在阐述。

RxJava的优点

说了那么多,那到底RxJava有啥好处呢,异步和简洁应该是它最大的优点,它可以随时随地的切换线程,同时又能保证代码的清晰简明.至于它是怎么做到的,后面我会一点一点的来分析.

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

分分快三计划 1

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

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

 

线程调度(Scheduler)

说到这线程调度嘛,这就是RxJava另一个牛逼的地方了;利用RxJava给的Scheduler咱们可以随时随地的切换线程,这就是RxJava实现异步的方法,具体怎么使用呢,且听我娓娓道来.

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

如果想查询更多资料可以参考这个链接

背压(Backpressure)

2.请求权限:

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

 

Flowable的几种Backpressure策略

MISSING
如果流的速度无法保持同步,可能会抛出MissingBackpressureException或IllegalStateException。
BUFFER
上游不断的发出onNext请求,直到下游处理完,也就是和Observable一样了,缓存池无限大,最后直到程序崩溃
ERROR
会在下游跟不上速度时抛出MissingBackpressureException。
DROP
会在下游跟不上速度时把onNext的值丢弃。
LATEST
会一直保留最新的onNext的值,直到被下游消费掉。

下面咱们通过一段代码来具体分析

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {
                for(int i = 0; i < 129;   i){
                    e.onNext(i);
                }
                e.onComplete();

            }
        }, BackpressureStrategy.ERROR)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(3);
                    }
                    @Override
                    public void onNext(Integer s) {
                        Log.d(TAG, "onNext: "   s);
                    }
                    @Override
                    public void onError(Throwable t) {
                        Log.d(TAG, "onError" t);
                    }
                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");

                    }
                });

在Flowable中背压采取拉取响应的方式来进行水流控制,也就是说Subscription是控制上下游水流的主要方式,一般的,我们需要调用Subscription.request()来传入一个下游目前能处理的事件的数量,不过你不传也是可以的,因为在Flowable内部已经设置了一个默认值(128),具体的可以去看Flowable的源码,如上面所写,我设置了s.request(3);表示下游只能出来3个事件,而上游我传了129个事件进来,下游显然是无法处理的,因此程序会抛出MissingBackpressureException异常;如果感兴趣的同学可以试试其他几种策略模式,这里就不再赘述了.

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

 

观察者模式

既然提到了RxJava的核心是观察者模式,那么这里就简单的说说什么是观察者模式.观察者模式完美的将观察者和被观察的对象分离开,在这种模式中有两个对象:观察者(Observer)和被观察者(Observerable),他们两个通过订阅(Subscribe)的方式产生联系,当两者建立起联系以后,那么当被观察者作出一些变化之后,观察者能够立即获知到,并根据被观察者作出的变化作出相应的反应.
举个例子:护士和病人即观察者和被观察者,当病人不舒服的时候就按铃,护士听到铃声以后赶来照顾病人,它们之间通过铃作为纽带把它们联系到一起,这样一来护士就不需要时时刻刻盯着病人

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()方法。

  项目中用到了Angular5,其中遇到了需要同时发起两个request查询结果,这两个request的结果需要同时返回才能进行后面的逻辑处理,由于js是异步的,如果仅仅在代码中按照顺序执行,必然会出现一个request先返回,这样就会出现逻辑上的混乱,得不到想要的结果。通过一番搜索总结使用了Observable中的forkJoin解决了以上问题。具体细节如下:

RxJava是什么

对于RxJava,官方给的说法是一个使用Java虚拟机观察序列异步和基于事件的程序库;绕过这些官方语言,以我自己的话来说,它就是一个用观察者模式对程序进行异步控制的这么一个库.

第二步:

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。

分分快三计划 2

我们可以看到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的键值对。这样如果我们重复申请某个权限的时候,我们直接返回了一个创建一个不发射任何数据但是正常终止的`Observable`:`Observable.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));
}

我们来分析上面这段核心代码:

fetchData0=function():  Observable<any>{
        return http.get(url0);
   }
fetchData1=function():  Observable<any>{
        return http.get(url1);
   }
let fetchData0 = this.services.fetchData0();
let fetchData1 = this.services.fetchData1();
let source = Observable.forkJoin(fetchData0, fetchData1);
source.subscribe(
    data => {
    this.logicFetchData0=data[0];
    this.logicFetchData1=data[1]
      },error => {
    console.log('failure to get data')
});  

操作符

可以说RxJava那么受欢迎,很大程度得益于操作符的存在,操作符可以让事件队列在发送的过程中进行转换加工处理,比如你创建的时候往队列里放了一个香蕉,但是经过中间转换以后,香蕉变成了苹果,这么说可能会比较抽象,下面我就简单的介绍几个常用操作符,来加深一下对它的理解

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

案例

在看代码前,有几个概念需要先说明一下,subscribeOn()observeOn() 这两个方法可以对线程进行切换,而具体切换到哪个线程则是由调度器Scheduler来控制,下面讲讲两个方法的使用以及作用范围.
observeOn
observeOn()可以多次调用,一般放在map和flatmap等操作符前,用来控制操作符中的操作,作用范围在下一个observeOn出现前

 observeable.just(T...)
            .observeOn(Schedulers.io())
            .map(1)
            .flatMap(2);
            .observeOn(Schedulers.newThread())
            .map(3)
            .subscribe(4)

结果是1、2是在io线程里执行的;3、4是在newThread线程里执行的
subscribeOn
subscribeOn()位置放在哪里都行,但只能调用一次;一般就是指定Observable创建和doOnSubscribe的线程;比如:Observeable.create(...);Observeable.fromArray(...)或者Observeable.doOnSubscribe(...)都由subscribeOn来控制,如果程序中没有调用过observeOn,而只调用了subscribeOn,那么程序里所有的mapflatMap都会在subscribeOn指定的线程中执行直到出现下个observeOn

Observable.fromArray(...)
          .map(1)
          .flatMap(2)
          .subscribeOn(Schedulers.io())

1、2都是在io线程中执行, 除非在程序中某处执行了observeOn

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

入门案例

实现RxJava的步骤分三步
第一步:创建被观察者(Observable)
第二步:创建观察者(Observer)
第三步:订阅(Subscribe),即让观察者和被观察者产生联系
基于以上理论,下面我们用代码来演示一下
1.创建Observable

Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
        e.onNext("123");
        e.onComplete();
    }
});

2.创建Observer

Observer<String> observer = new Observer<String>() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        Log.i(TAG,"onSubscribe");
    }

    @Override
    public void onNext(@NonNull String s) {
        Log.i(TAG,"onNext:" s);
    }

    @Override
    public void onError(@NonNull Throwable e) {
        Log.i(TAG,"onError");
    }

    @Override
    public void onComplete() {
        Log.i(TAG,"onComplete");
    }
};

3.订阅

observable.subscribe(observer);

最终输出结果为:onSubscribe、onNext:123、onComplete,可以看到在创建Observable的时候,传入了一个ObservableOnSubscribe对象作为参数,它决定了事件序列执行顺序,而observer里的方法决定了,执行内容,最后通过订阅将两个对象绑定在一起
create() 是最基本的一种创建Observeable的方法,基于这个方法,RxJava为我们提供了很多种方法来创建事件队列.
fromArray(T...item) 传入多个参数,并将这些参数依次发给观察者
just(T...)fromArray(T...item) 差不多

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()方法,这个方法上面已经介绍过了。这里也不多说了。可以文章往上拉一下看看。

flatMap

flatMap算是一个比较有用的变换,它的作用和map类似,但是我觉得它又比map高级多了,到底是哪里高级呢,下面我们通过一段代码来分析一下

Observable.fromArray("1","2")
        .flatMap(new Function<String, Observable<String>>() {
            @Override
            public Observable<String> apply(@NonNull String s) throws Exception {
                Log.e(TAG,"原来的事件:" s);
                return Observable.fromArray("一","二");
            }
        })
        .subscribe(new Consumer<String>() {

            @Override
            public void accept(@NonNull String s) throws Exception {
                Log.i(TAG,"改造后的事件:" s);
            }
        });

打印的Log如下

分分快三计划 3

打印的Log

从结果看出mapflatMap类似,都是对数据进行了变换,不同的是flatMap返回的是Observeable对象,我们可以把之前的事件处理以后放到一个新的Observeable 中,需要注意的是这个新的Observeable 并不是直接发送事件,它还是被放到了原先的Observeable 中由原先的Observeable 来发送事件,下面画个图来理解一下这个过程

分分快三计划 4

这里写图片描述

从图里可以看出,原先的Observeable序列中的事件经过变换,又变成了一个新的序列,最后再由原先Observeable把这些事件统一交给Subscriber的回调方法

当然了,类似的操作符还有很多,比如zip操作符,它就是把多个数据流合并到一处,最终发送到一个地方;总而言之,操作符的作用就是在事件发送过程中对数据进行处理的.

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修饰的方法。

概述

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来分别对每个权限的申请结果来进行处理

map

Observable.fromArray("小明")      //输入内容
        .map(new Function<String, String>() {   //map转换
            @Override
            public String apply(@NonNull String s) throws Exception {
                return s   "的爸爸";
            }
        })
        .subscribe(new Consumer<String>() {     //订阅
            @Override
            public void accept(@NonNull String s) throws Exception {
                Log.i(TAG, s);
            }
        });

这里出现了几个新东西Function,Consumer,咱们一一来解释其作用,首先是Function<T,R>,它就是一个接口,作用是传入一个值,然后传出另一个值,T代表传入值的类型,R代表传出值的类型,用它可以对输入值进行一些运算,返回一些其他值.map的作用就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列.而Consumer<T>则是一个接受单一值的函数接口,作用类似于Observer里的onNext 用来处理接收过来的值;现在我们可以这样来理解:Observable原先的序列中仅仅存储了小明这个事件,而如今经过map的变换,就把事件源中的小明改造成了小明的爸爸这一事件

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,则说明申请权限被拒绝了。

环境搭建

1.RxJava的GitHub地址:https://github.com/ReactiveX/RxJava
2.RxAndroid的GitHub地址:https://github.com/ReactiveX/RxAndroid
3.导入Jar包

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:latest.version'

rxandroid是一个对rxjava的扩展库,这个库里包含了一些和Android有关的内容,下面我会具体介绍到

easyPermission:

前面我们已经讲了如何用没有使用封装的库直接用原生API来进行权限申请。接下去我们要介绍easyPermisson;
** 相关github链接:easyPermisson**。

这个库是谷歌推出的,所以我也使用过这个。怎么使用这个库我就不多说了。我们直接来看相关的源码分析。

我们直接看他官方给的Demo:

入门

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

结语

看完上面的介绍,首先你得明白我们到底拿RxJava来做什么,怎么做,有啥好处,如果你把上面的内容都看懂了,那么恭喜你你已经入门了;剩下的只需要在实际开发中去应用了,最后:如果有写的不对的地方还请见谅,也欢迎各位大神指正.

Android 动态申请权限:

这里我会分三大块来讲:

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

常用的Scheduler

调度器类型 作用范围
AndroidSchedulers.mainThread() AndroidUI线程
Schedulers.io() IO线程(读写文件、数据库和网络信息交互等)都可以放在IO线程里来执行,它的模式和newThread()差不多,但效率比newThread()高
Schedulers.newThread() 开启一个新线程
Schedulers.single() 单例线程,可以把两段程序放在同一个线程里顺序执行

分分快三计划 5

概念

在rxjava中会经常遇到一种情况就是被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息。那么随之而来的就是如何处理这些未处理的消息。举个例子,使用zip操作符将两个无限大的Observable压缩在一起,其中一个被观察者发送消息的速度是另一个的两倍。一个比较不靠谱的做法就是把发送比较快的消息缓存起来,当比较慢的Observable发送消息的时候取出来并将他们结合在一起。这样做就使得rxjava变得笨重而且十分占用系统资源。

解决这个问题的方法就是使用背压策略,在RxJava1.0中,如果使用Observeable的时候产生了背压问题系统是会抛MissingBackpressureException异常的,而在RxJava2.0中Observable不再支持背压,多出了Flowable(也是被观察者)来支持背压,当然了这里既然提到了一个新的被观察者,那么就不得不提它的搭档观察者了Subscriber 通常这两个是一起使用的,下面来看看使用Flowable 是如何使用背压策略的
Flowable.create(FlowableOnSubscribe<T> source, BackpressureStrategy mode)

4.请求权限提示:

android6.0(API23)及以上,提供了一个方法:shouldshowrequestpermissionrationale(),如果应用程序请求此权限,并且用户拒绝了请求,则此方法返回true。

ps:用户在过去拒绝了权限请求,对话框中选择了“不再询问”选项,该方法返回false。如果设置中禁止应用程序具有该权限,该方法还将返回false。

所以我们可以当用户拒绝了这个权限的时候,我们可以用这个方法判断,然后可以弹出一个弹框,并且写上我们的提示内容,比如我们可以弹出一个弹框,上面写着“如果要使用我们的XXX功能,一定要开启XXX权限哦!!!!”,然后同时我们可以让点击弹框的“确认”按钮后跳到“设置”应用,让用户手动去打开权限。

我们也可以在Activity中覆写该方法:

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

前言

第一次接触RxJava是在学习Retrofit的时候,那个时候经常看到别人都是Retrofit RxJava一起使用的,于是后来自己也上网研究了一下,经过一段时间的学习总算是把RxJava给弄懂了,在这里就分享一下我的使用心得,给想入门的同学引一引路.

第一步:

RxPermissions rxPermissions = new RxPermissions(this);
我们可以看到我们实例化了RxPermissions的对象了。

前言

  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)我们需要动态去申请的时候,我们需要动态去获取。比如:
身体传感器
日历
摄像头
通讯录
地理位置
麦克风
电话
短信
存储空间

主要是上述几大块相关的权限,需要动态去获取,具体见下面列表:

分分快三计划 6

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:安装时不会获得权限,可以在运行时向用户申请权限。用户授权以后仍然可以在设置界面中取消授权。

原生API支持:

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

本文由分分快三计划发布,转载请注明来源

关键词: 分分快三计划 程序员 首页投稿 Android专栏