您的位置:首页 > 移动开发 > Android开发

RxPermissions源码分析:使用RxJava处理Android 6.0运行时动态权限获取

2016-11-11 20:06 841 查看
项目地址:RxPermissions,本文分析版本:

4c4d4e1

1.简介

RxPermissions是基于
RxJava
开发的用于帮助在
Android 6.0
中处理运行时权限检测的框架。在
Android 6.0
中,系统新增了部分权限的运行时动态获取。而不再是在以前的版本中安装的时候授予权限。

对于运行时的权限获取提示,国内的
Android
工程师们应该并不陌生,国内的第三方
ROM
例如
MIUI
在很早前就做了类似的功能。但是第三方
ROM
并不能提供给我们权限请求成功或失败的接口,这就导致我们无法通过
PackageManager
提供的
checkPermission()
方法来准确的获取到我们是否获得该权限。只能根据具体的权限来做相应的处理。但是在
Android
6.0
中我们可以准确的获取我们的应用是否获取某个权限,具体的方法希望大家看这篇文章:Android 6.0 运行时权限处理。大致方法是通过
API 23
中的
Activity
requestPermissions(String[] permissions, int requestCode);
方法请求权限,并在
onRequestPermissionsResult(int
requestCode,String[] permissions,int[] grantResults)
回调方法中处理请求结果。这个方法与
onActivityResult()
类似。
RxPermissions
在这个基础上做了封装,使我们在处理运行时权限变得更加的简单。

另外关于
RxJava
如果你现在还没了解过
RxJava
可以直接翻到文章最下面去查看我总结的一些
RxJava
相关的文章,不然并不推荐直接看这篇文章。下面我们就来具体看看
RxPermissions
的使用方法以及源码分析。

2.使用方法

1.直接获取权限(使用Retrolambda使代码更加简洁,当然并不是必须使用):

// 必须在初始化阶段调用,例如onCreate()方法中
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(granted -> {
if (granted) { // 在android 6.0之前会默认返回true
// 已经获取权限
} else {
// 未获取权限
}
});

2.通过条件触发获取权限(结合RxBinding使用)

// 必须在初始化阶段调用,例如onCreate()方法中
RxView.clicks(findViewById(R.id.enableCamera))
.compose(RxPermissions.getInstance(this).ensure(Manifest.permission.CAMERA))
.subscribe(granted -> {
// 当R.id.enableCamera被点击的时候触发获取权限
});

3.一次请求多个权限(有两种方式)

如果同时请求多个权限,下面这种方式会合并请求结果,即所有权限请求成功会返回
true
,若有一个权限未成功则返回
false


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

当然你可以通过
requestEach
or
ensureEach
来分别获取每一个权限请求的结果。

RxPermissions.getInstance(this)
.requestEach(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(permission -> { // 会发送两个Permission对象
if (permission.granted) {
// `permission.name` is granted !
}
});

注意:由于在请求权限的过程中
app
有可能会被重启,所以权限请求必须放在初始化的阶段,比如在
Activity.onCreate/onResume
, 或者

View.onFinishInflate
方法中。如果不这样处理,那么如果
app
在请求过程中重启的话,权限请求结果将不会发送给订阅者即
subscriber


2.整体介绍

RxPermission
一共就只有三个类:
Permission
是定义的权限
model
类,用来存放权限名称以及是否获取权限的信息。
RxPermissions
就是最主要的类了,利用
RxJava
提供了我们上面在使用方法中介绍的所有方法。还有一个
ShadowActivity
类是用来请求权限用的。下面我们就来详细介绍
RxPermission
的实现。

注意:如果你还未了解过
RxJava
那么可以先阅读本文最后的一系列优秀文章。如果你已经了解过
RxJava
的话,那么下面我将会介绍
RxJava
中的部分操作符在
RxPermission
中的实际运用。相信能帮助你更好的理解
RxJava
中操作符的使用。

3.源码分析

我们依然按照我们惯用的方法来分析,通过使用方法来分析调用流程,最终理解整个项目。首先再回顾一遍使用方法(注意这里我同时请求了两个权限),那么结果将是如果所有权限请求成功会返回
true
,若有一个权限未成功则返回
false
。代码如下:

// 必须在初始化阶段调用,例如onCreate()方法中
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(granted -> {
if (granted) { // 在android 6.0之前会默认返回true
// 已经获取权限
} else {
// 未获取权限
}
});

1.RxPermissions.getInstance(this)的实现

static RxPermissions sSingleton;
private Context :;

public static RxPermissions getInstance(Context) {
if (sSingleton == null) {
sSingleton = new RxPermissions(ctx.getApplicationContext());
}
return sSingleton;
}

RxPermissions(Context ctx) {
mCtx = ctx;
}

很明显是维护
RxPermissions
的单例,不再多做介绍,紧接着我们来看
RxPermissions
request(Manifest.permission.CAMERA)
方法的实现:

2.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)的实现:

public Observable<Boolean> request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}

首先从返回值看到是返回一个
Observable<Boolean>
对象,方法中也是直接
return
Observable.just(null).compose(ensure(permissions))
。这里涉及两个方法分别是
just()
以及
compose()
我们先解释这两个操作符:

1.Observable.just(null)
just
操作符可以将某个对象转化为
Observable
对象,并且将其发射出去,可以是一个数字、一个字符串、数组、Iterate对象等,是
RxJava
中非常快捷的创建
Observable
对象的方法。在这里
just()
方法中虽然传入的是
null
但是并不影响创建出的
Observable
的作用,如果有
subscriber
订阅依然会依次调用其
onNext()
onCompleted()
方法。所以这里就是为了创建出一个
Observable
对象,便于后续的处理。创建完
Observable
对象之后紧接着调用了
compose()
方法:

2.compose(Transformer)操作符
compose()
操作符是针对
Observable
自身的变换,通过我们自己定义的
Transformer
对象可以将对
Observable
对象变换的操作封装起来,实现一个简单的
Transformer
对象如下:

class myTransformer implements Observable.Transformer<Object, Boolean> {

@Override
public Observable<Boolean> call(Observable<Object> objectObservable) {
return objectObservable.map(new Func1<Object, Boolean>() {
@Override
public Boolean call(Object o) {
return true;
}
});
}
}

通过上面这个
Transformer
就可以将任何
Observable<Object>
对象转换成
Observable<Boolean>
对象了。当然在
Transformer
里你也可以返回一个全新的
Observable
对象。
RxPermissions
就是这样做的,那么回到项目中再来看
compose(ensure(permissions));
那么
ensure(permissions);
一定是返回一个
Transformer
对象了。

3.ensure(permissions);方法的实现
我们来看看
ensure(permissions)
方法的实现:

public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
//创建一个Transformer对象返回
return new Observable.Transformer<Object, Boolean>() {
// o表示当前Observable对象。
@Override
public Observable<Boolean> call(Observable<Object> o) {
//request(o, permissions) 方法返回 Observable<Permission>对象
return request(o, permissions)
// 将Observable<Permission>转换为Observable<Boolean>
// buffer操作符
.buffer(permissions.length)
// flatMap操作符
.flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(List<Permission> permissions) {
// 如果permissions为空那么直接返回Observable.empty();
if (permissions.isEmpty()) {
// Occurs during orientation change, when the subject receives onComplete.
// In that case we don't want to propagate that empty list to the
// subscriber, only the onComplete.
return Observable.empty();
}
// 遍历所有Permission,如果有一个未成功则返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});
}
};
}

确实是返回一个
Observable.Transformer
对象,那么在
call()
方法里首先调用了
request(o, permissions)
方法,然后又进行了
buffer()
flatMap()
的处理,最终会返回
Observable.empty();
Observable.just(false);
Observable.just(true);
对象。我们先来看看
request(o,
permissions)
方法的实现:

4.request(o, permissions);方法的实现
private Observable<Permission> request(final Observable<?> trigger,
final String... permissions) {
//如果并没有请求的权限则抛出异常
if (permissions == null || permissions.length == 0) {
throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");
}
return oneOf(trigger, pending(permissions))
.flatMap(new Func1<Object, Observable<Permission>>() {
@Override
public Observable<Permission> call(Object o) {
return request_(permissions);
}
});
}

首先对
permissions
做了判断,然后调用了
oneOf(trigger, pending(permissions))
方法,并通过
flatMap()
操作符在
call()
方法中调用了
request_(permissions);
按照惯例我们应该去看
oneOf()
方法的实现了。但是
oneOf()
方法里其实并没有什么实际意义,看了项目的
commit
log
我觉得应该是历史遗留问题,作者可能是想处理一些相同重复的权限请求,但是并没有实现。所以其实这个方法完全可以这样代替;

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

直接调用
request_(permissions);
即可。我测试中并没有发现问题。目前我还没有联系到作者询问这个方法的实现目的,稍后可能会提一个
issue
,如果有结果会在文章中更新。所以这里大家就完全可以理解成直接调用了
request_(permissions);
方法:

5.request_(permissions);方法的实现
@TargetApi(Build.VERSION_CODES.M)
private Observable<Permission> request_(final String... permissions) {

//创建出一个存放Observable<Permission>的list
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
//存放还为请求权限的list
List<String> unrequestedPermissions = new ArrayList<>();

// 在请求多个权限的时候,我们为每一个请求的权限都创建一个observable对象,在最后
// 这些observable会被合并成一个response。
for (String permission : permissions) {
log("Requesting permission " + permission);
//如果是已经获得的权限,或者Android版本在6.0之前则直接添加一个
// Observable.just(new Permission(permission, true))对象.
if (isGranted(permission)) {
// Already granted, or not Android M
// Return a granted Permission object.
list.add(Observable.just(new Permission(permission, true)));
continue;
}
// 如果是已经拒绝的权限则添加
// Observable.just(new Permission(permission, false))对象.
if (isRevoked(permission)) {
// Revoked by a policy, return a denied Permission object.
list.add(Observable.just(new Permission(permission, false)));
continue;
}

PublishSubject<Permission> subject = mSubjects.get(permission);
// 如果mSubjects 不存在当前 permission,则添加到unrequestedPermissions中
// 并且创建PublishSubject对象并添加到mSubjects中。
if (subject == null) {
unrequestedPermissions.add(permission);
subject = PublishSubject.create();
mSubjects.put(permission, subject);
}
//并且添加到list中
list.add(subject);
}

//如果有未请求的权限
if (!unrequestedPermissions.isEmpty()) {
startShadowActivity(unrequestedPermissions
.toArray(new String[unrequestedPermissions.size()]));
}
return Observable.concat(Observable.from(list));
}

void startShadowActivity(String[] permissions) {
log("startShadowActivity " + TextUtils.join(", ", permissions));
Intent intent = new Intent(mCtx, ShadowActivity.class);
intent.putExtra("permissions", permissions);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCtx.startActivity(intent);
}

代码如上,注释非常清晰,整体上就是将已经允许的权限和已经拒绝过的权限添加到
list
中,并且将还未请求的权限分别添加到
mSubjects
list
中。然后调用
startShadowActivity();
方法。最后通过
Observable.concat(Observable.from(list));
返回。这里主要包含
PublishSubject
concat()
操作符的知识:

6.PublishSubject对象
PublishSubject
文档中,可以看出是继承自Subject,
Subject
是既可以充当
Observer
又能充当
Observable
的。从文档中的
Example
中可以看到订阅
PublishSubject
observer
只会接收到订阅之后
PublishSubject
发送的数据,但是本项目中并没有体现出此特性,主要是利用
PublishSubject
中的
onNext()
onCompleted()
方法,这里大致了解这么多,下面是
Example
的代码:

PublishSubject<Object> subject = PublishSubject.create();
// observer1 will receive all onNext and onCompleted events
subject.subscribe(observer1);
subject.onNext("one");
subject.onNext("two");
// observer2 will only receive "three" and onCompleted
subject.subscribe(observer2);
subject.onNext("three");
subject.onCompleted();

7.concat()操作符
Concat
操作符将多个
Observable
结合成一个
Observable
并发射数据,并且严格按照先后顺序发射数据,前一个
Observable
的数据没有发射完,是不能发射后面
Observable
的数据的。引用自此篇文章。所以在本项目中
Concat()
是为了保证请求的权限按顺序返回。接下来我们看看
ShadowActivity
的实现:

8.ShadowActivity的实现
@TargetApi(Build.VERSION_CODES.M)
public class ShadowActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
handleIntent(getIntent());
}
}

@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}

private void handleIntent(Intent intent) {
String[] permissions = intent.getStringArrayExtra("permissions");
requestPermissions(permissions, 42);
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
RxPermissions.getInstance(this).onRequestPermissionsResult(requestCode, permissions, grantResults);
finish();
}
}

很简单其实就是按照系统提供给我们的方法进行权限请求,最后回调
RxPermissions
onRequestPermissionsResult()
方法:

9.onRequestPermissionsResult()方法的实现
void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
for (int i = 0, size = permissions.length; i < size; i++) {
log("onRequestPermissionsResult  " + permissions[i]);
// 取出对应的PublishSubject对象
PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
if (subject == null) {
// No subject found
throw new IllegalStateException("RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
}
//从mSubjects移除
mSubjects.remove(permissions[i]);
//获取结果
boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
//调用onNext()方法发送结果
subject.onNext(new Permission(permissions[i], granted));
//调用onCompleted()方法。
subject.onCompleted();
}
}

简单的来说就是拿到结果并发送结果。所以就又回到了最初的
ensure(permissions);
方法中的
request(o, permissions)
之后,代码如下:

request(o, permissions)
// 将Observable<Permission>转换为Observable<Boolean>
// buffer操作符
.buffer(permissions.length)
// flatMap操作符
.flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(List<Permission> permissions) {
// 如果permissions为空那么直接返回Observable.empty();
if (permissions.isEmpty()) {
// Occurs during orientation change, when the subject receives onComplete.
// In that case we don't want to propagate that empty list to the
// subscriber, only the onComplete.
return Observable.empty();
}
// 遍历所有Permission,如果有一个未成功则返回false,全部成功返回true。
for (Permission p : permissions) {
if (!p.granted) {
return Observable.just(false);
}
}
return Observable.just(true);
}
});

所以这里会不断的发送
Observable<Permission>
对象,请求了几个权限就会发送几次,但是这里用了一个
buffer()
操作符,关于
buffer()
操作符:

10.buffer()操作符
buffer
英文是缓冲区的意思。所以Buffer操作符所要做的事情就是将数据按照规定的大小做一下缓存,然后将缓存的数据作为一个集合发射出去。详细可以看这里,所以在本项目中就是讲这些
Observable<Permission>
转换成
Observable<List<Permission>>
对象,紧接着又使用了
flatMap()
操作符然后返回了我们最终的结果。以上就是整个的调用流程了,如果有不清楚的建议可以多多的调试
RxPermission
的代码以及查阅各种资料帮助理解。

11. requestEach()、ensureEach()、ensure()的实现
以上我们分析了
request()
方法的实现,看似好像还剩下上面三个方法没有分析。其实仔细看的同学应该已经看明白了。上面三个方法其实都是差不多的。如果你看懂了
request()
方法的实现,那么这三个方法你一定能看懂,有兴趣的同学可以自行去源码里研究。

4.个人评价

其实
Android 6.0
的权限处理我自己在项目中都没有使用过,因为拿目前国内市场来说,首先
Android 6.0
的手机占有量非常少。再者我们可以使用很简单的方法将
targetSdkVersion
设置为
22
来兼容
6.0
的权限处理。所以目前项目中应该很少需要使用
RxPermissions
这个项目。但是这个项目作为
RxJava
的学习资料是相当的好。从中我们可以学到大量的
RxJava
相关的使用知识。如果你现在在学习
RxJava
,强烈推荐这个项目。

5.RxJava相关文章

RxJava

深入浅出RxJava(大头鬼教父的文章)

给 Android 开发者的 RxJava 详解

RxJava 与 Retrofit 结合的最佳实践

RxJava的操作符(有四篇)

RxJava操作符 (有八篇)

那些年我们错过的响应式编程

不要打破链式:使用Rxjava的compose()操作符

当EventBus遇上RxJava

Rx小鄧子的简书相当多RxJava相关文章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: