Android6.0权限机制使用回顾

去年的时候,公司的系统版本升级到了Android6.0,在维护之前的产品的时候,发现了关于权限管理的相关代码实现的不是很好。于是,我果断上第三方Jar。
这里写图片描述

直接请求权限

如图:
这里写图片描述

//点击Camera按钮
findViewById(R.id.button_camera).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cameraTask();
            }
        });
//判断有没有申请权限
private boolean hasCameraPermission() {
        return EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA);
    }

 @AfterPermissionGranted(RC_CAMERA_PERM)
    public void cameraTask() {
        if (hasCameraPermission()) {
            // Have permission, do the thing!
            Toast.makeText(this, "TODO: Camera things", Toast.LENGTH_LONG).show();
        } else {
            // 请求一个权限
            EasyPermissions.requestPermissions(
                    this,
                    getString(R.string.rationale_camera),
                    RC_CAMERA_PERM,
                    Manifest.permission.CAMERA);
        }
    }        

请求权限的时候,通过EasyPermissions调用requestPermissions方法,然后将需要的权限值传入就可以了。下面我们理一下这些类中都做了什么。

EasyPermissions

public class EasyPermissions {

    /**
     *权限申请成功与失败的回调
     */
    public interface PermissionCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback {

        void onPermissionsGranted(int requestCode, @NonNull List<String> perms);

        void onPermissionsDenied(int requestCode, @NonNull List<String> perms);

    }

    private static final String TAG = "EasyPermissions";

    /**
     * 检查权限
     */
    public static boolean hasPermissions(@NonNull Context context,
                                         @Size(min = 1) @NonNull String... perms) {
        //如果版本小于6.0,直接return
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {

            return true;
        }
        //没有上下文直接抛出异常
        if (context == null) {
            throw new IllegalArgumentException("Can't check permissions for null context");
        }
        //检查有没有授予权限
        for (String perm : perms) {
            if (ContextCompat.checkSelfPermission(context, perm)
                    != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }

        return true;
    }

    //请求权限,传入需要请求权限的上下文等各种配置信息
    @Deprecated
    public static void requestPermissions(
            @NonNull Fragment host, @NonNull String rationale,
            @StringRes int positiveButton, @StringRes int negativeButton,
            int requestCode, @Size(min = 1) @NonNull String... perms) {
        requestPermissions(
                new PermissionRequest.Builder(host, requestCode, perms)
                        .setRationale(rationale)
                        .setPositiveButtonText(positiveButton)
                        .setNegativeButtonText(negativeButton)
                        .build());
    }

   // 委托请求权限
    public static void requestPermissions(PermissionRequest request) {

      ...省略代码...


        request.getHelper().requestPermissions(
                request.getRationale(),
                request.getPositiveButtonText(),
                request.getNegativeButtonText(),
                request.getTheme(),
                request.getRequestCode(),
                request.getPerms());
    }

 ...省略代码...
}

EasyPermissions主要封装了请求权限与各种条件下的Dialog弹窗回调信息,让调用者使用更加方便。通过PermissionRequest里面的PermissionHelper 来进行真正的权限请求,前者是模型对象,后者是对象帮助类。

PermissionRequest

public final class PermissionRequest {
    private final PermissionHelper mHelper;
    private final String[] mPerms;//权限数组,可以是一个,也可以是多个
    private final int mRequestCode;//权限值
    private final String mRationale;//文字描述
    private final String mPositiveButtonText;//确定按钮描述
    private final String mNegativeButtonText;//取消按钮描述
    private final int mTheme;//主题
    //构造方法中传入值
    private PermissionRequest(PermissionHelper helper,
                              String[] perms,
                              int requestCode,
                              String rationale,
                              String positiveButtonText,
                              String negativeButtonText,
                              int theme) {
        mHelper = helper;
        mPerms = perms.clone();
        mRequestCode = requestCode;
        mRationale = rationale;
        mPositiveButtonText = positiveButtonText;
        mNegativeButtonText = negativeButtonText;
        mTheme = theme;
    }
    //
    @NonNull
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public PermissionHelper getHelper() {
        return mHelper;
    }

    /**
     * 建造者模式,生产具有各种选项的权限请求
     *
     * @see PermissionRequest
     */
    public static final class Builder {
        private final PermissionHelper mHelper;
        private final int mRequestCode;
        private final String[] mPerms;

        private String mRationale;
        private String mPositiveButtonText;
        private String mNegativeButtonText;
        private int mTheme = -1;

        /**
         * 获取PermissionHelper的实例
         *  用于Activity
         *
         */
        public Builder(@NonNull Activity activity, int requestCode,
                       @NonNull @Size(min = 1) String... perms) {
            mHelper = PermissionHelper.newInstance(activity);
            mRequestCode = requestCode;
            mPerms = perms;
        }

        ...省略代码...

        /**
         *
         * 如果用户拒绝了请求的权限,再次请求权限的时候,那么就显示一个理由说明。
         * 如果用户永久拒绝请求的权限,那么请使用
         * use the {@link AppSettingsDialog}
         * <p>
         * 默认显示的具体文本
         *
         */
        @NonNull
        public Builder setRationale(@Nullable String rationale) {
            mRationale = rationale;
            return this;
        }

        /**
         *生产权限请求
         */
        @NonNull
        public PermissionRequest build() {

            ...省略代码...

            return new PermissionRequest(
                    mHelper,
                    mPerms,
                    mRequestCode,
                    mRationale,
                    mPositiveButtonText,
                    mNegativeButtonText,
                    mTheme);
        }
    }
}

PermissionRequest中,又调用了PermissionHelperrequestPermissions方法,而PermissionHelper是个抽象类,库的作者为了方便管理,根据请求权限的上下文,分别抽象出了AppCompatActivityPermissionHelperFrameworkFragmentPermissionHelper这些类来真正的请求对应的权限

PermissionHelper

/**
 * 委托类,供Activity fragment 代理调用
 */
public abstract class PermissionHelper<T> {

    private T mHost;
    //自动判断当前的上下文是什么
    @NonNull
    public static PermissionHelper<? extends Activity> newInstance(Activity host) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return new LowApiPermissionsHelper<>(host);
        }

        if (host instanceof AppCompatActivity)
            return new AppCompatActivityPermissionHelper((AppCompatActivity) host);
        else {
            return new ActivityPermissionHelper(host);
        }
    }

    ...省略代码...

    // ============================================================================
    // Public concrete methods
    // ============================================================================

    public PermissionHelper(@NonNull T host) {
        mHost = host;
    }

    //判断是否需要显示说明权限的弹窗
    private boolean shouldShowRationale(@NonNull String... perms) {
        for (String perm : perms) {
            if (shouldShowRequestPermissionRationale(perm)) {
                return true;
            }
        }
        return false;
    }
    //请求权限,这里调用抽象方法。
    public void requestPermissions(@NonNull String rationale,
                                   @NonNull String positiveButton,
                                   @NonNull String negativeButton,
                                   @StyleRes int theme,
                                   int requestCode,
                                   @NonNull String... perms) {
        if (shouldShowRationale(perms)) {
            Log.e("PermissionHelper","showRequestPermissionRationale");
            //显示权限的提示框
            showRequestPermissionRationale(
                    rationale, positiveButton, negativeButton, theme, requestCode, perms);

        } else {
            Log.e("PermissionHelper","directRequestPermissions");
            //直接显示请求的权限框
            directRequestPermissions(requestCode, perms);
        }
    }

    @NonNull
    public T getHost() {
        return mHost;
    }

    // ============================================================================
    // Public abstract methods
    // ============================================================================

    public abstract void directRequestPermissions(int requestCode, @NonNull String... perms);

    public abstract boolean shouldShowRequestPermissionRationale(@NonNull String perm);

    public abstract void showRequestPermissionRationale(@NonNull String rationale,
                                                        @NonNull String positiveButton,
                                                        @NonNull String negativeButton,
                                                        @StyleRes int theme,
                                                        int requestCode,
                                                        @NonNull String... perms);

    public abstract Context getContext();

}

抽象方法showRequestPermissionRationaledirectRequestPermissions分别显示两种不同的弹窗状态。PermissionHelper可以作为基类让其他子类实现弹出窗的逻辑。

public abstract class BaseFrameworkPermissionsHelper<T> extends PermissionHelper<T> {

    public BaseFrameworkPermissionsHelper(@NonNull T host) {
        super(host);
    }

    public abstract FragmentManager getFragmentManager();

    @Override
    public void showRequestPermissionRationale(@NonNull String rationale,
                                               @NonNull String positiveButton,
                                               @NonNull String negativeButton,
                                               @StyleRes int theme,
                                               int requestCode,
                                               @NonNull String... perms) {
        RationaleDialogFragment
                .newInstance(positiveButton, negativeButton, rationale, theme, requestCode, perms)
                .showAllowingStateLoss(getFragmentManager(), RationaleDialogFragment.TAG);
    }
}




class ActivityPermissionHelper extends BaseFrameworkPermissionsHelper<Activity> {

    public ActivityPermissionHelper(Activity host) {
        super(host);
    }

    @Override
    public FragmentManager getFragmentManager() {
        return getHost().getFragmentManager();
    }
   // 真正请求权限的地方,直接请求权限
    @Override
    public void directRequestPermissions(int requestCode, @NonNull String... perms) {
        ActivityCompat.requestPermissions(getHost(), perms, requestCode);
    }
   //显示权限说明的弹框
    @Override
    public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
        return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
    }

    @Override
    public Context getContext() {
        return getHost();
    }
}

Builder模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

OK,上面贴了这么多代码。也就处理了第一种情况,直接请求权限。

拒绝权限,再次申请权限

当用户拒绝了权限之后,为了更好的用户体验,我们可以弹出框,说明一下,为什么要请求这个权限。

如下图:
这里写图片描述

在代码中,基本调用流程和上面一样,只不过在PermissionHelper请求权限的时候,添加一个判断方法shouldShowRationale,来让系统判定是需要为用户说明权限的重要性。

 private boolean shouldShowRationale(@NonNull String... perms) {
        for (String perm : perms) {
            if (shouldShowRequestPermissionRationale(perm)) {
                return true;
            }
        }
        return false;
    }

这个判断,会让继承抽象类的子类去覆写对应的方法。例如在AppCompatActivityPermissionHelper类中:

    @Override
    public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
        return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
    }

用户点击了不在询问按钮

如果用户不耐烦的点击了不在询问的选项,那么这个权限弹窗就弹不出来了,那这样就不给用户使用这个权限了吗?当然不行。比如我们请求摄像头,用户不小心点击了不在询问,那我们也应该再次询问用户,你怎么重新获取权限等方式。或者直接跳转到系统权限列表界面。
如下图:
这里写图片描述

具体的流程是这样的:用户点击了“不在询问”之后,也就是权限申请失败了,那么会通过EasyPermissions.PermissionCallbacks回调到onPermissionsDenied方法中,这个时候,我们再给他弹个窗,来让用户点击。具体的判断用户是否点击了“不在询问”看代码

//PermissionHelper方法中
 public boolean somePermissionPermanentlyDenied(@NonNull List<String> perms) {
        for (String deniedPermission : perms) {
            if (permissionPermanentlyDenied(deniedPermission)) {
                return true;
            }
        }

        return false;
    }
   //如果用户点击了不在询问之后,那么也不会显示权限具体说明的弹框了。所以通过这个来设置布尔值
   //shouldShowRequestPermissionRationale方法是抽象方法,在子类中去具体实现,和上面两种情况一样
    public boolean permissionPermanentlyDenied(@NonNull String perms) {
        return !shouldShowRequestPermissionRationale(perms);
    }

点击确定之后,弹出设置的界面。如下代码:

 startActivityForResult(
                    new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                            .setData(Uri.fromParts("package", getPackageName(), null)),
                    APP_SETTINGS_RC);

那么这个库叫什么呢?大家都用过easypermissions
最近出差,生病了一周,现在的身体就像一团卫生纸一样,一扣就烂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值