动态权限的使用以及RxPermissions源码分析

基本使用

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 ensure(final String... permissions) {
    return new Observable.Transformer() {
        @Override
        public Observable call(Observable o) {
            return request(o, permissions)
            // 将Observable转换成Observable
            .buffer(permissions.length)
            .flatMap(new Func1, Observable>() {
                @Override
                public Observable call(List permissions) {
                    if (permissions.isEmpty()) {
                        // 如果申请的权限列表为空,则返回一个只回调onComplete()方法的Observable对象
                        return Observable.empty();
                    }
                    // 循环权限列表,判断权限是否申请成功
                    for (Permission p : permissions) {
                        if (!p.granted) {
                            return Observable.just(false);
                        }
                    }
                    return Observable.just(true);
                }
            });
        }
    };
}

上面代码的作用是传入一个Observable对象,然后转换成一个Observable对象,而这个Observable对象就是前面Observable.just(null)所创建的对象,同时ensure()方法传入了permissions权限申请列表,在转换的初步,就是需要根据permissions权限列表去申请权限,具体操作在request()方法中,下面看看request()的实现

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对象,看看代码
@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以上,动态权限就变成了我们日常开发中必须要做的工作,那么为了避免编写重复代码,就需要使用到第三方库来简化我们的操作,但是我们在使用第三方库的时候还是需要懂得其实现的原理,这样使用起来会更加的方便,它好的思想也可以促进我们编码水平的提供,最后就是,如果有特别的需求,我们也可以在其库的基础上进行特别的定制,来适应自身的需求,这也是看其源码的目的和意义

你可能感兴趣的:(动态权限的使用以及RxPermissions源码分析)