Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android6.0权限管理到RxPermissions源码分析

$
0
0

在给应用适配6.0版本的时候,我们就需要运行时权限管理。在6.0开始有一套新的运行机制管理用于更友好的保护用户的隐私安全,一般涉及用户隐私的需要实时来提示用户通过允许和拒绝来授权。

如何申请一个权限呢?
1.在AndroidManifest中把我们需要的权限添加,像我那天忘记加了一点就闪退,呼!奔溃了好久。
2.检查权限

if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
        // 已经申请同意了就直接处理逻辑代码
}else{
    // 走申请流程
}

3.申请这个权限

ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

4.在Activity中处理这个申请回调

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // 如果权限被取消了,这个数组会是空的
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // 权限被允许了  接着处理下一步逻辑

            } else {

                // 权限被拒绝   可以做些友好的引导处理
            }
            return;
        }
    }
}

OK、一整段的流程下来之后我瞬间感觉这么写反正我不喜欢,跟onActivityResult的回调似的,太不喜欢了。然后就在github上搜索一下,发现了RxPermissions这么一个开源库而且是基于RxJava瞬间就被吸引了,接下来我们就聊聊这个库。

RxPermissions简单用法介绍

// 必须在类似onCreate的方法中进行初始化
RxPermissions.getInstance(this)
    .request(Manifest.permission.CAMERA)
    .subscribe(granted -> {
        if (granted) { // M版本之前都是返回true
           // 权限允许
        } else {
           // 权限被拒绝
        }
    });

这是一段简单用法并且基于RxJava以及在Retrolambda条件下看起来的模样,当然这里是你需要的权限每一个都同意之后才回调到回来,当然你也可以一个一个处理回调。

RxPermissions.getInstance(this)
    .requestEach(Manifest.permission.CAMERA,
             Manifest.permission.READ_PHONE_STATE)
    .subscribe(permission -> { // 将会发射多个权限请求
        if (permission.granted) {
           // 权限允许
        }
    });

更多用法可以查看https://github.com/tbruyelle/RxPermissions
接下来点进去查看源码,看他是如何封装权限管理的。

    static RxPermissions sSingleton;

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

    private Context mCtx;

没错RxPermissions是采用单例模式,传入ctx.getApplicationContext()是因为权限允许与否是回调回来,存在可能得Context泄露所以传入的是ApplicationContext对象,接下我们就连着调用request方法。

/**
     * 立即请求权限,调用必须在应用程序的初始化阶段
     */
    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(null).compose(ensure(permissions));
    }

我们可以看到是走入ensure这个方法,permissions这个参数就是我们传入的权限数组,compose操作法需要一个Observable.Transformer来将我们前面的Observable转成我们想要的Observable类似于flatmap操作符,我们可以看到返回的结果是Observable.Transformer< Object, Boolean >也就是把Observable.just(null)转成我们想要的Observable< Boolean >,所以我们进入到ensure这个方法看是如何转换的。

public Observable.Transformer<Object, Boolean> ensure(final String... permissions) {
        return new Observable.Transformer<Object, Boolean>() {
            @Override
            public Observable<Boolean> call(Observable<Object> o) {
                return request(o, permissions)
                        // 将 Observable<Permission> 转换成 Observable<Boolean>
                        .buffer(permissions.length)
                        .flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
                            @Override
                            public Observable<Boolean> call(List<Permission> permissions) {
                                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();
                                }
                                // 所有的权限被允许的时候返回true
                                for (Permission p : permissions) {
                                    if (!p.granted) {
                                        return Observable.just(false);
                                    }
                                }
                                return Observable.just(true);
                            }
                        });
            }
        };
    }

根据传入的参数一开始是进入request这个方法中去,二话不说直接跟着进去看看情况。

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

继续二话不说跳入oneOf这个方法中去,可是我们发现参数中pending(permissions)有这个一个方法,所以我们先进去看这个方法。

    private Observable<?> pending(final String... permissions) {
        for (String p : permissions) {
            if (!mSubjects.containsKey(p)) {
                return Observable.empty();
            }
        }
        return Observable.just(null);
    }

这时候又迷糊了mSubjects这里面是什么东西呢?

    // Contains all the current permission requests.
    // Once granted or denied, they are removed from it.
    private Map<String, PublishSubject<Permission>> mSubjects = new HashMap<>();

从声明的注释里面来看,存放的就是当前的权限请求数组,允许或者拒绝都将从里面移除。这里需要解释一下Observable.empty()表示的是:不发射任何数据并正常结束的Observable。所以pending里面要么返回empty要么返回null的Observable,根据后面的代码可以看到只有我们请求一个不存在的权限的时候会抛出错误从而没有在mSubjects中remove,然后我们再进入oneOf:

    private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
        if (trigger == null) {
            return Observable.just(null);
        }
        return Observable.merge(trigger, pending);
    }

所以这边会返回一个Observable.just(null)、或者返回Observable.merge(trigger, pending)的一个合并的Observable然后一个一个的发射出去。然后进入最重要的request_方法中去:

    private Observable<Permission> request_(final String... permissions) {

        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        List<String> unrequestedPermissions = new ArrayList<>();

        // In case of multiple permissions, we create a observable for each of them.
        // At the end, the observables are combined to have a unique response.
        for (String permission : permissions) {
            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)));
                continue;
            }

            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);
            // Create a new subject if not exists
            if (subject == null) {
                unrequestedPermissions.add(permission);
                subject = PublishSubject.create();
                mSubjects.put(permission, subject);
            }

            list.add(subject);
        }

        if (!unrequestedPermissions.isEmpty()) {
            startShadowActivity(unrequestedPermissions
                    .toArray(new String[unrequestedPermissions.size()]));
        }
        return Observable.concat(Observable.from(list));
    }

for循环我们要请求的权限数组,通过isGranted_先判断是否已经权限被允许了。

    private boolean isGranted_(String permission) {
        return mCtx.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

通过isRevoked_方法来确认是否权限被取消了

    private boolean isRevoked_(String permission) {
        return mCtx.getPackageManager().isPermissionRevokedByPolicy(permission, mCtx.getPackageName());
    }

然后会判断是否已经在mSubjects权限列表中了,如果没有加入unrequestedPermissions这个还没被请求的列表中然后去启动ShadowActivity来进行权限授权。

        if (!unrequestedPermissions.isEmpty()) {
            startShadowActivity(unrequestedPermissions
                    .toArray(new String[unrequestedPermissions.size()]));
        }

在Activity中处理Intent请求拿出需要请求的权限

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

然后回调权限请求结果到onRequestPermissionsResult中

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

然后执行到onRequestPermissionsResult(引用RxPermissions是一个单列所以对象是同一个的)把授权的结果返回出去。

    void onRequestPermissionsResult(int requestCode,
                                    String permissions[], int[] grantResults) {
        for (int i = 0, size = permissions.length; i < size; i++) {
            log("onRequestPermissionsResult  " + permissions[i]);
            // Find the corresponding subject
            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.remove(permissions[i]);
            boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
            subject.onNext(new Permission(permissions[i], granted));
            subject.onCompleted();
        }
    }

是不是觉得很奇怪这边发射了数据出去然后我们在ensure这个方法通过subscribe也发射数据,按我的理解呢onRequestPermissionsResult这里面的发射数据我们并没有去做接收,更多的是确认当前这次的权限请求是否被允许了。然后无乱结果与否我们都返回到request_这个方法中去,并且通过concat把结果返回给ensure。

return Observable.concat(Observable.from(list));

concat主要作用是将多个Observable连续发射出去。

buffer(permissions.length)

我们看到ensure中用到这么一个操作符,这个操作符类似一个缓存区也就是说一下子把缓冲区中的数据一下子全部发射出去而不是一个一个发射,而request跟requestEach的区别就是这与这个buffer操作。

flatMap(new Func1<List<Permission>, Observable<Boolean>>() {
                            @Override
                            public Observable<Boolean> call(List<Permission> permissions) {
                                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();
                                }
                                // Return true if all permissions are granted.
                                for (Permission p : permissions) {
                                    if (!p.granted) {
                                        return Observable.just(false);
                                    }
                                }
                                return Observable.just(true);
                            }
                        });

然后通过flatMap来判断任何一个权限被拒绝了就会返回false全部通过才返回true。
上面的是对权限请求必须每一个都同意后才能通过,如果我们使用requestEach来权限请求的话就不同了只要有允许就发射出去。

    public Observable.Transformer<Object, Permission> ensureEach(final String... permissions) {
        return new Observable.Transformer<Object, Permission>() {
            @Override
            public Observable<Permission> call(Observable<Object> o) {
                return request(o, permissions);
            }
        };
    }

因为requestEach返回的用concat连接起来的Observable所以会依次发射权限数据出去。
大概的RxPermissons的源码就是这么个流程,当然里面涉及了一些RxJava的操作符而且该库也支持RxJava2.0了,这样封装在项目中使用就更加方便了。

RxPermissions项目地址:https://github.com/tbruyelle/RxPermissions
参考文章:http://blog.csdn.net/lmj623565791/article/details/50709663

作者:Neacy_Zz 发表于2016/10/5 0:16:24 原文链接
阅读:95 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>