基本使用
Android6.0之后,增加了动态权限配置,目的在于用户可以自由的选择自己是否给予app权限,就算没有给予某个权限,也不影响其他功能的使用,不至于令用户无法安装
接下来先看一下基本的使用,代码如下:
// 检测是否授予了CALL_PHONE这个权限
if (ContextCompat.checkSelfPermission(MainActivity@ this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 还未授予,则申请权限
ActivityCompat.requestPermissions(MainActivity@ this, arrayOf(Manifest.permission.CALL_PHONE), 0x111)
} else {
// 已经授予,则进行相关操作
call()
}
// 该方法是申请权限后的回调方法
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
// 请求码对应
0x111 -> {
// 判断申请权限是否成功
if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 成功申请,执行相关操作
call()
} else {
// 申请失败,提示用户
Toast.makeText(this, "您拒绝了该权限", Toast.LENGTH_SHORT).show()
}
}
else -> {
}
}
}
// 权限申请成功后的相关操作
private fun call() {
try {
val intent = Intent(Intent.ACTION_CALL)
intent.setData(Uri.parse("tel:10086"))
startActivity(intent)
} catch (e: SecurityException) {
e.printStackTrace()
}
}
以上是申请权限的基本操作,操作的方式其实类似于startActivityForResult()这种启动Activity的方法,也是会有具体的回调方法,但是如果每次都需要这样写,未免太过于繁琐,所以一般情况都需要对其进行封装,而RxPermissions这个库就对其进行了很好的封装,下面就使用RxPermissions进行实现
使用之前需要在项目中添加相关依赖
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
下面是详细实现代码
// 定义一个RxPermission对象
private lateinit var mRxPermission:RxPermissions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建Permission对象
mRxPermission = RxPermissions(this)
// 申请权限
mRxPermission.request(Manifest.permission.CALL_PHONE).subscribe(object :Action1{
override fun call(t: Boolean?) {
// 回调之后的操作
if (t!!){
call()
}else{
Toast.makeText(this@MainActivity, "您拒绝了该权限", Toast.LENGTH_SHORT).show() }
}
})
相比一开始的代码,利用RxPermissions写的简洁了很多,也无需重写onRequestPermissionsResult()方法,这就是使用了该库的好处,节省了我们的代码量,也使代码变得更加清晰有条理,那么它是怎么做到的呢,下面就对其源码进行解读
1、其实,RxPermission采用的方式是利用了一个隐形的Fragment来请求权限,然后在回调中用RxJava进行数据的组装和转化,最后变成了布尔类型的数据回调回来,下面是具体的分析
// 创建RxPermissions的对象
mRxPermission = RxPermissions(this)
// 定义RxPermissions的对象
RxPermissionsFragment mRxPermissionsFragment;
// RxPermissions的构造方法
public RxPermissions(@NonNull Activity activity) {
// 获取Fragment的实例
mRxPermissionsFragment = getRxPermissionsFragment(activity);
}
// 获取Fragment的方法
private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
// 查询是否已经存在了该Fragment,这样是为了让该Fragment只有一个实例
RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
boolean isNewInstance = rxPermissionsFragment == null;
// 如果还没有存在,则创建Fragment,并添加到Activity中
if (isNewInstance) {
rxPermissionsFragment = new RxPermissionsFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(rxPermissionsFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return rxPermissionsFragment;
}
// 利用tag去找是否已经有该Fragment的实例
private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {
return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
通过以上的代码可以知道,在创建RxPermissions的对象中,其实就是获取Fragment的实例而已,既然这样,我们就需要到这个Fragment的实现中,看它在被创建的时候做了什么事情
// Fragment的构造方法
public RxPermissionsFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 禁止横竖屏切换时的Fragment的重建
setRetainInstance(true);
}
可以看到,在Fragment被创建时,并没有重写onCreateView()方法,来进行布局文件的加载,只是重写了onCreate()方法,然后禁止了横竖屏Fragment的重建,这就代表说,这个Fragment是一个没有布局的隐形Fragment,不会在屏幕上展示出来,但是这个Fragment却是关键,权限的申请与申请结果的回调都是在Fragment中完成的,这样,我们才不需要为申请结果重写回调方法
2、接下来是具体的请求
mRxPermission.request(Manifest.permission.CALL_PHONE).subscribe(object :Action1{
override fun call(t: Boolean?) {
if (t!!){
call()
}else{
Toast.makeText(this@MainActivity, "您拒绝了该权限", Toast.LENGTH_SHORT).show()
}
}
})
上面的代码主要是调用RxPermissions对象中的request()方法,然后该方法会返回一个Observable,下面看具体的request()方法代码
@SuppressWarnings({"WeakerAccess", "unused"})
public Observable request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}
首先,利用just(null)方法创建Observable对象
接下来会调用compose()方法。compose()的参数就为Observable.Transformer对象,该对象的作用是将一个类型的Observable对象转换成另外一个类型的Observable对象,同时也可以对自身对象的一些重复操作进行封装,避免重复编写代码
public Observable.Transformer
上面代码的作用是传入一个Observable
private Observable 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");
}
// 首先,合并了两个Observable对象,然后通过flatMap将Object对象转换成Observable对象
return oneOf(trigger, pending(permissions))
.flatMap(new Func1>() {
@Override
public Observable call(Object o) {
return requestImplementation(permissions);
}
});
}
通过上面代码可以看出,首先会判断申请权限列表是否为空,如果为空就会抛出异常,然后通过oneOf方法和pending()方法来创建合并Observable对象,下面看一下这两个方法
private Observable> pending(final String... permissions) {
// 循环遍历,查询该权限是否已经在申请过了
for (String p : permissions) {
if (!mRxPermissionsFragment.containsByPermission(p)) {
// 如果列表中有一个权限未在Fragment的HashMap集合中保存
// 则返回Observeble.empty(),返回的这个Observable对象
// 只会调用onComplete()方法,所以并不会进入flatMap等操作// 符号中
return Observable.empty();
}
}
return Observable.just(null);
}
private Observable> oneOf(Observable> trigger, Observable> pending) {
// 判断Observable对象是否为null,在这里其实是不太可能会为null的
if (trigger == null) {
return Observable.just(null);
}
//返回合并的Observable对象
return Observable.merge(trigger, pending);
}
从以上的两个方法的代码可以看出,pending()主要是判断权限申请列表中是否全部都在Fragment中的mSubjects集合中,如果有一个不在集合中,则返回Observable.empty()方法,如果已经全部在集合中,则返回Observable.just(null),然后在oneOf()方法中根据trigger是否为null来判断是返回Observable.just(null)还是Observable.merge(trigger, pending)。这两个方法在我看来最终并没有起到实际的作用,因为具体的请求权限是在requestImplementation(permissions)方法中实现的,而通过以上两个方法得到的Observable对象最终在flatMap操作转换时并不会用到它们的对象,而是直接根据权限申请列表“permissions”作为参数,直接调用requestImplementation()方法,进行权限的实际请求,所以接下来主要看看requestImplementation()方法的具体实现
private Observable requestImplementation(final String... permissions) {
// 创建集合保存已经申请的权限
List> list = new ArrayList<>(permissions.length);
// 创建集合保存未申请的权限
List unrequestedPermissions = new ArrayList<>();
// 循环权限申请列表
for (String permission : permissions) {
mRxPermissionsFragment.log("Requesting permission " + permission);
// 如果权限已经申请过了,则直接保存到集合中
if (isGranted(permission)) {
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;
}
// 先去RxPermissionsFragment中查询是否已经存在了该权限
PublishSubject subject = mRxPermissionsFragment.getSubjectByPermission(permission);
// Create a new subject if not exists
// 如果还未存在,则创建一个PublishSubject对象
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);
}
// 利用list集合创建Observable对象,并且用concat进行链接,返回一个Observable对象
return Observable.concat(Observable.from(list));
}
上面的就是进行权限申请的具体代码,主要的操作就是定义一个集合保存保存一次申请的所有权限,无论这个权限是否已经申请,还是被撤销,还是未申请,最终都会保存到list这个集合中,这样,我们在后续的操作中,才可以进行转换,同时,定义一个集合,用于保存未申请的权限,然后在循环结束之后进行未申请权限的申请。接下来看看requestPermissionsFromFragment()放到,它最终调用的还是mRxPermissionsFragment.requestPermissions(permissions)方法,就是RxPermissionsFragment中的requestPermissions方法了,所以我们进到该方法看看
void requestPermissions(@NonNull String[] permissions) {
requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
}
可以看到,该方法并没有进行什么特别操作,就是申请权限,那么既然申请了权限了,是否申请成功的处理应该在回调中,所以我们看看回调的处理
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 如果请求码不符合,则直接返回
if (requestCode != PERMISSIONS_REQUEST_CODE) return;
// 以权限列表的长度为容器的size创建一个boolean数组
boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
// 循环遍历,看权限是否被永久拒绝了
for (int i = 0; i < permissions.length; i++) {
// 这里会调用Fragment中的shouldShowRequestPermissionRationale()方法,然后这个方法如果是申请成功会返回true,如果被点击了不在提醒,并且拒绝权限时,则会返回false
shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);
}
// 调用重载方法
onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);
}
// 重载方法
void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
// 循环权限列表
for (int i = 0, size = permissions.length; i < size; i++) {
log("onRequestPermissionsResult " + permissions[i]);
// Find the corresponding subject
// 查询mSubjects集合中是否存在代表该permission的PublishSubject对象
PublishSubject subject = mSubjects.get(permissions[i]);
// 如果没有,则直接返回
if (subject == null) {
// No subject found
Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
return;
}
// 将集合中的permission的PublishSubject对象进行移除
mSubjects.remove(permissions[i]);
// 判断是否申请成功
boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
// 返回相应的对象
subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
subject.onCompleted();
}
}
上面的代码是回调处理权限申请的全部过程,主要用到了两个方法,第一个方法主要用于创建一个boolean数组,用于保存判断权限申请的状态,如果权限被申请,并且用户在对话框中点击了“不在提醒”选项,这个时候shouldShowRequestPermissionRationale()这个方法还是会返回false,代表说这个权限被拒绝,我们可以根据这个在给用户做一些提示性的工作。然后就到了重载的方法了,在重载的方法中,首先判断在mSubjects集合中是否已经存在了该permission的PublishSubject对象,如果没有,则直接返回,代表出错,但是这里的对象在RxPermissions#requestImplementation()中已经通过mRxPermissionsFragment.setSubjectForPermission(permission, subject);语句进行赋值了,所以一定会是存在的。接着mSubjects集合将该permission的PublishSubjects对象移除,这是为什么呢,这个要看RxPermissions#requestImplementation()中的实现,在该方法中会PublishSubject
subject = mRxPermissionsFragment.getSubjectByPermission(permission);这个语句出现,这个语句代表说,如果权限未申请过,也未被撤销,那么就直接在RxPermissionsFragment的mSubjects集合中查找是否存在该permission的PublishSubject对象,如果有,就直接通过list.add(subject);这个语句保存到集合中了,那么这样被拒绝的权限,下次将不会被重新申请,所以需要移除,之后就是发射数据,发送时间结束标识了
分析完了RxPermissionsFragment中的权限申请的操作,我们就需要回到Rxpermissions#request()方法中,在该方法的flatMap的回调方法中返回了Observable对象,因此这个方法的任务完成,接着再往回看,在ensure()方法中,我们可以看到在该方法中调用了request()方法之后,接着调用了buffer(permissions.length)方法,其实就是Observable对象调用了buffer()方法,那么这个buffer()的作用是什么呢,它的作为是为了将一个序列的Observable对象转换成Observable>对象,为什么要这样转换,就需要看requestImplementation()方法中的返回值,该方法返回值为Observable.concat(Observable.from(list));这代表说这是一个Observable对象序列,所以需要通过buffer()方法进行转换,接着就使用flatMap操作符转换成Observable对象,看看代码
- >对象,为什么要这样转换,就需要看requestImplementation()方法中的返回值,该方法返回值为Observable.concat(Observable.from(list));这代表说这是一个Observable
@Override
public Observable call(List permissions) {
// 如果集合为空,代表说没有权限申请,直接返回Observable.empty()
if (permissions.isEmpty()) {
return Observable.empty();
}
// 循环遍历集合
for (Permission p : permissions) {
// 如果权限列表中有一个权限申请失败,则直接返回申请失败
if (!p.granted) {
return Observable.just(false);
}
}
// 全部申请成功,则返回申请成功
return Observable.just(true);
}
通过以上代码可以看到,最终会遍历Permissions集合,这个集合其实就是一开始我们调用request(final String... permissions)这个方法之后的权限,经过处理返回的Observable
序列对象,这里是库提供的一个默认实现,它会遍历集合,如果有一个权限申请失败,都当作是申请权限失败了,但是我们也可以自己来决定返回后的Observable 对象要怎么处理,那就是调用requestEach()方法,然后最终得到的就是一个未经过转换成Observable 处理的Observable 对象,我们可以自己根据实际需要进行必要的处理,而放弃掉默认提供的实现,不过一般情况下,使用默认的实现已经足够
总结
如今大部分的Android手机设备的系统都在6.0以上,动态权限就变成了我们日常开发中必须要做的工作,那么为了避免编写重复代码,就需要使用到第三方库来简化我们的操作,但是我们在使用第三方库的时候还是需要懂得其实现的原理,这样使用起来会更加的方便,它好的思想也可以促进我们编码水平的提供,最后就是,如果有特别的需求,我们也可以在其库的基础上进行特别的定制,来适应自身的需求,这也是看其源码的目的和意义