Android6.0权限与RxPermissions关系

作者: 乱码桑 | 来源:发表于2017-05-04 15:23 被阅读745次

    前言

    Android6.0之后开始对权限进行控制。

    如果你把targetSdkVersion设置大于等于23时,需要运行时获取权限。
    设置targetSdkVersion设置小于23,系统默认开启所有权限。这个也是Android给出的一个的兼容方案。

    鉴于现在市面上大部分手机都是4.x,5.x,6.x的手机占有量还是少的。

    大部分的开发者做法:将targetSdkVersion设置为22,默认全部开启权限

    我以targetSdkVersion设置为23举例
    代码如下:

    // REQUEST_CODE
    private static final int REQUEST_CODE = 1;
    
    ...
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         // 判断是否开启权限,没有则去申请权限
        if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED){
            //申请WRITE_EXTERNAL_STORAGE权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_CODE);
        }
    
    }
    
    // 权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == REQUEST_CODE){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                Log.d(TAG,">>>>>granted");
            }else{
                Log.d(TAG,">>>>>deny");
            }
        }
    }
    

    通过上面可以知道,在启动的时候需要去检查权限,申请权限,处理返回结果
    上面用的是v4包的兼容方式调用,为了兼容Android6.0以下的手机

    不是所有权限都要首页进行申请,有些可以到指定页面在申请,如拍照权限

    关于使用到的API

    1.ActivityCompat.checkSelfPermission(权限名)
    检查是否有权限
    
    2.ActivityCompat.shouldShowRequestPermissionRationale(权限名) 
    返回true表示在询问用户权限的对话框 用户未选择 不要再询问,反之则已选择不要再询问。
    
    3.ActivityCompat.requestPermissions
    请求授权
    
    4.onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    回调获取授权结果,判断是否授权
    

    回到本文的重心,RxPermissions

    RxPermission介绍

    RxPermissions是基于Rxjava实现的Android 6.0中处理运行时权限检测的框架。

    吐槽:
    现在RxJava很火,有很多它的实现框架,RxAndroid RxBus RxPermissions Rxbinding RxLifeCircle等

    说白了,就是用RxJava的方式实现上述方法的使用,将方法用的更简洁。如果你觉得理解RxPermissions麻烦的话,我建议还是按照Android的API方式调用会更好

    RxPermission的git地址:https://github.com/tbruyelle/RxPermissions

    RxPermissions的使用姿势

    发现网上说的RxPermissions的介绍都是关于老版本的介绍。

    repositories {
    jcenter() // If not already there
    }
    dependencies {
        compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0'
    }
    
    // Must be done during an initialization phase like onCreate
    RxPermissions.getInstance(this)
    .request(Manifest.permission.CAMERA)
    .subscribe(new Subscriber<Boolean>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(Boolean granted) {
                     if (granted) {
                           // All requested permissions are granted
                        } else {
                           // At least one permission is denied
                        }
                }
            });
    

    这个分支是fix46,大家可以去看下。

    作为一个程序员,当然要以最新的为准了。
    最新的使用姿势

    repositories {
    jcenter() // If not already there
    }
    
    dependencies {
        compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4'
    }
    
    RxPermissions rxPermissions = new RxPermissions(this);
    
    
    // Must be done during an initialization phase like onCreate
    rxPermissions
    .request(Manifest.permission.CAMERA)
    .subscribe(new Subscriber<Boolean>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(Boolean granted) {
                     if (granted) {
                           // All requested permissions are granted
                        } else {
                           // At least one permission is denied
                        }
                }
            });
    
    
    // 多个权限申请
    rxPermissions
    .request(Manifest.permission.CAMERA,
             Manifest.permission.READ_PHONE_STATE)
    .subscribe(new Subscriber<Boolean>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(Boolean granted) {
                     if (granted) {
                           // All requested permissions are granted
                        } else {
                           // At least one permission is denied
                        }
                }
            });
    

    具体可以看github介绍

    新版本主要是将RxPermissions.getInstance(this) -> new RxPermissions(this),现在不再使用单例
    至于原因:我猜应该是单例持有当前的Activity,容易造成内存泄漏吧

    姿势讲完了,看代码吧。

    .
    ├── AndroidManifest.xml
    └── java
        └── com
            └── tbruyelle
                └── rxpermissions
                    ├── Permission.java。--封装了的权限的基类,包含权限名称,是否授权等
                    ├── RxPermissions.java --核心处理类,进行rxjava转换
                    └── RxPermissionsFragment.java --实际权限请求的Fragment
    

    因为代码较少,我直接就贴代码了。添加了注释,大家就就这样将就看吧

    • Permission.java
    package com.tbruyelle.rxpermissions;
    
    /**
     * 权限的基类
     * 封装了权限名称 是否授权 受否显示请求授权的对话框
     *
     * ActivityCompat.checkSelfPermission --> granted
     * ActivityCompat.shouldShowRequestPermissionRationale -> shouldShowRequestPermissionRationale
     */
    public class Permission {
        public final String name;  // 权限名称
        public final boolean granted; // 是否授权
        public final boolean shouldShowRequestPermissionRationale; // 受否显示请求授权的对话框
    
        public Permission(String name, boolean granted) {
            this(name, granted, false);
        }
    
        public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) {
            this.name = name;
            this.granted = granted;
            this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale;
        }
    
        @Override
        @SuppressWarnings("SimplifiableIfStatement")
        public boolean equals(final Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            final Permission that = (Permission) o;
    
            if (granted != that.granted) return false;
            if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale)
                return false;
            return name.equals(that.name);
        }
    
        @Override
        public int hashCode() {
            int result = name.hashCode();
            result = 31 * result + (granted ? 1 : 0);
            result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0);
            return result;
        }
    
        @Override
        public String toString() {
            return "Permission{" +
                    "name='" + name + '\'' +
                    ", granted=" + granted +
                    ", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale +
                    '}';
        }
    }
    
    
    • RxPermissionsFragment.java
    package com.tbruyelle.rxpermissions;
    
    import android.annotation.TargetApi;
    import android.app.Fragment;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.annotation.NonNull;
    import android.util.Log;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import rx.subjects.PublishSubject;
    
    /**
     * RxPermissionsFragment用于权限的相关操作
     * 如调用requestPermission,onRequestPermissionsResult回调等
     *
     * 此Fragment直接调用Android6.0的fragment,兼容处理在RxPermission中实现
     */
    public class RxPermissionsFragment extends Fragment {
    
        /** requestPermission时用的REQUEST_CODE**/
        private static final int PERMISSIONS_REQUEST_CODE = 42;
    
        // Contains all the current permission requests.
        // Once granted or denied, they are removed from it.
        //  PublishSubject 是最直接主要的Subject实现,
        // 当一个事件值被发送给PublishSubject时,它会将这个事件值发送给订阅它的每个订阅者:
        private Map<String, PublishSubject<Permission>> mSubjects = new HashMap<>();
        /** 日志开关 **/
        private boolean mLogging;
    
        public RxPermissionsFragment() {
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 在 Activity 重绘时,我们的 Fragment 不会被重复绘制,也就是它会被“保留”。
            // 为了验证其作用,我们发现在设置为 true 状态时,旋转屏幕,Fragment 依然是之前的 Fragment。
            setRetainInstance(true);
        }
    
        /**
         * 发起请求Permissions
         * @param permissions
         */
        @TargetApi(Build.VERSION_CODES.M)
        void requestPermissions(@NonNull String[] permissions) {
            requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
        }
    
        /**
         * 请求Permissions的回调
         * @param requestCode
         * @param permissions
         * @param grantResults
         */
        @TargetApi(Build.VERSION_CODES.M)
        public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            if (requestCode != PERMISSIONS_REQUEST_CODE) return;
    
            boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
    
            for (int i = 0; i < permissions.length; i++) {
                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
                PublishSubject<Permission> 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;
                }
                mSubjects.remove(permissions[i]);
                boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
                subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
                subject.onCompleted();
            }
        }
    
        /**
         * 是否授权
         * @param permission
         * @return
         */
        @TargetApi(Build.VERSION_CODES.M)
        boolean isGranted(String permission) {
            return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
        }
    
        /**
         * 判断是不是在包中申明
         * @param permission
         * @return
         */
        @TargetApi(Build.VERSION_CODES.M)
        boolean isRevoked(String permission) {
            return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
        }
    
        public void setLogging(boolean logging) {
            mLogging = logging;
        }
    
        public PublishSubject<Permission> getSubjectByPermission(@NonNull String permission) {
            return mSubjects.get(permission);
        }
    
        public boolean containsByPermission(@NonNull String permission) {
            return mSubjects.containsKey(permission);
        }
    
        public PublishSubject<Permission> setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject<Permission> subject) {
            return mSubjects.put(permission, subject);
        }
    
        void log(String message) {
            if (mLogging) {
                Log.d(RxPermissions.TAG, message);
            }
        }
    
    }
    
    
    • RxPermissions.java
    /**
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * <p>
     * http://www.apache.org/licenses/LICENSE-2.0
     * <p>
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.tbruyelle.rxpermissions;
    
    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.os.Build;
    import android.support.annotation.NonNull;
    import android.text.TextUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import rx.Observable;
    import rx.functions.Func1;
    import rx.subjects.PublishSubject;
    
    /**
     * RxPermissions处理类
     */
    public class RxPermissions {
    
        static final String TAG = "RxPermissions";
    
        /**
         * 为什么要使用RxPermissionsFragment???
         * 通过这个fragment进行权限的操作
         */
        RxPermissionsFragment mRxPermissionsFragment;
    
        public RxPermissions(@NonNull Activity activity) {
            mRxPermissionsFragment = getRxPermissionsFragment(activity);
        }
    
        /**
         * 生成RxPermissionsFragment,用来获取Fragment对象
         * @param activity
         * @return
         */
        private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
    
            // 在当前Activity添加一个Fragment,然后通过这个Fragment进行权限处理
    
            RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
            boolean isNewInstance = rxPermissionsFragment == null;
            if (isNewInstance) {
                rxPermissionsFragment = new RxPermissionsFragment();
                FragmentManager fragmentManager = activity.getFragmentManager();
                fragmentManager
                        .beginTransaction()
                        .add(rxPermissionsFragment, TAG)
                        .commitAllowingStateLoss();
                fragmentManager.executePendingTransactions();
            }
            return rxPermissionsFragment;
        }
    
        private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {
            return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
        }
    
        /**
         * 设置打印日志
         * @param logging
         */
        public void setLogging(boolean logging) {
            mRxPermissionsFragment.setLogging(logging);
        }
    
        /**
         * Map emitted items from the source observable into {@code true} if permissions in parameters
         * are granted, or {@code false} if not.
         * <p>
         * If one or several permissions have never been requested, invoke the related framework method
         * to ask the user if he allows the permissions.
         */
        @SuppressWarnings("WeakerAccess")
        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)
                            // Transform Observable<Permission> to 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();
                                    }
                                    // Return true if all permissions are granted.
                                    for (Permission p : permissions) {
                                        if (!p.granted) {
                                            return Observable.just(false);
                                        }
                                    }
                                    return Observable.just(true);
                                }
                            });
                }
            };
        }
    
        /**
         * Map emitted items from the source observable into {@link Permission} objects for each
         * permission in parameters.
         * <p>
         * If one or several permissions have never been requested, invoke the related framework method
         * to ask the user if he allows the permissions.
         */
        @SuppressWarnings("WeakerAccess")
        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);
                }
            };
        }
    
        /**
         * Request permissions immediately, <b>must be invoked during initialization phase
         * of your application</b>.
         */
        @SuppressWarnings({"WeakerAccess", "unused"})
        public Observable<Boolean> request(final String... permissions) {
            return Observable.just(null).compose(ensure(permissions));
        }
    
        /**
         * Request permissions immediately, <b>must be invoked during initialization phase
         * of your application</b>.
         */
        @SuppressWarnings({"WeakerAccess", "unused"})
        public Observable<Permission> requestEach(final String... permissions) {
            return Observable.just(null).compose(ensureEach(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 requestImplementation(permissions);
                        }
                    });
        }
    
        private Observable<?> pending(final String... permissions) {
            for (String p : permissions) {
                if (!mRxPermissionsFragment.containsByPermission(p)) {
                    return Observable.empty();
                }
            }
            return Observable.just(null);
        }
    
        private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
            if (trigger == null) {
                return Observable.just(null);
            }
            return Observable.merge(trigger, pending);
        }
    
        @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.from(list));
        }
    
        /**
         * Invokes Activity.shouldShowRequestPermissionRationale and wraps
         * the returned value in an observable.
         * <p>
         * In case of multiple permissions, only emits true if
         * Activity.shouldShowRequestPermissionRationale returned true for
         * all revoked permissions.
         * <p>
         * You shouldn't call this method if all permissions have been granted.
         * <p>
         * For SDK < 23, the observable will always emit false.
         */
        @SuppressWarnings("WeakerAccess")
        public Observable<Boolean> shouldShowRequestPermissionRationale(final Activity activity, final String... permissions) {
            if (!isMarshmallow()) {
                return Observable.just(false);
            }
            return Observable.just(shouldShowRequestPermissionRationaleImplementation(activity, permissions));
        }
    
        @TargetApi(Build.VERSION_CODES.M)
        private boolean shouldShowRequestPermissionRationaleImplementation(final Activity activity, final String... permissions) {
            for (String p : permissions) {
                if (!isGranted(p) && !activity.shouldShowRequestPermissionRationale(p)) {
                    return false;
                }
            }
            return true;
        }
    
        @TargetApi(Build.VERSION_CODES.M)
        void requestPermissionsFromFragment(String[] permissions) {
            mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
            mRxPermissionsFragment.requestPermissions(permissions);
        }
    
        /**
         *
         * 是否授权兼容处理
         * Android6.0以下,都为true
         * @param permission
         * @return
         */
        @SuppressWarnings("WeakerAccess")
        public boolean isGranted(String permission) {
            return !isMarshmallow() || mRxPermissionsFragment.isGranted(permission);
        }
    
        /**
         * 权限是否在包中
         * 兼容处理
         * Android6.0以下都为false
         * @param permission
         * @return
         */
        @SuppressWarnings("WeakerAccess")
        public boolean isRevoked(String permission) {
            return isMarshmallow() && mRxPermissionsFragment.isRevoked(permission);
        }
    
        /**
         * 是否为Android6.0以上的包
         * @return
         */
        boolean isMarshmallow() {
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
        }
    
        void onRequestPermissionsResult(String permissions[], int[] grantResults) {
            mRxPermissionsFragment.onRequestPermissionsResult(permissions, grantResults, new boolean[permissions.length]);
        }
    
    }
    
    

    就像刚开始介绍的一样,只是用Rxjava方式封装了这个权限请求的过程,如果对Rxjava很熟悉,建议使用。

    里面使用了很多RxJava的方法,这里不展开讲了。具体API看下方

    ReactiveX/RxJava文档中文版

    不是很擅长的话,推荐看下这篇权限请求的封装 android 6.0 Permission权限兼容的封装

    相关资料

    ReactiveX/RxJava文档中文版

    Android6.0权限适配的那些坑

    Android M 新的运行时权限开发者需要知道的一切

    安卓6.0权限适配 ----RxPermissions

    相关文章

      网友评论

      本文标题:Android6.0权限与RxPermissions关系

      本文链接:https://www.haomeiwen.com/subject/hrxdtxtx.html