Android6.0权限与RxPermissions关系

前言

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() {
            @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() {
            @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() {
            @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> 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 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 getSubjectByPermission(@NonNull String permission) {
        return mSubjects.get(permission);
    }

    public boolean containsByPermission(@NonNull String permission) {
        return mSubjects.containsKey(permission);
    }

    public PublishSubject setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject 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
 * 

* http://www.apache.org/licenses/LICENSE-2.0 *

* 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. *

* 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 ensure(final String... permissions) { return new Observable.Transformer() { @Override public Observable call(Observable o) { return request(o, permissions) // Transform Observable to Observable .buffer(permissions.length) .flatMap(new Func1, Observable>() { @Override public Observable call(List 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. *

* 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 ensureEach(final String... permissions) { return new Observable.Transformer() { @Override public Observable call(Observable o) { return request(o, permissions); } }; } /** * Request permissions immediately, must be invoked during initialization phase * of your application. */ @SuppressWarnings({"WeakerAccess", "unused"}) public Observable request(final String... permissions) { return Observable.just(null).compose(ensure(permissions)); } /** * Request permissions immediately, must be invoked during initialization phase * of your application. */ @SuppressWarnings({"WeakerAccess", "unused"}) public Observable requestEach(final String... permissions) { return Observable.just(null).compose(ensureEach(permissions)); } 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"); } return oneOf(trigger, pending(permissions)) .flatMap(new Func1>() { @Override public Observable 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 requestImplementation(final String... permissions) { List> list = new ArrayList<>(permissions.length); List 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 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. *

* In case of multiple permissions, only emits true if * Activity.shouldShowRequestPermissionRationale returned true for * all revoked permissions. *

* You shouldn't call this method if all permissions have been granted. *

* For SDK < 23, the observable will always emit false. */ @SuppressWarnings("WeakerAccess") public Observable 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关系)