Android 6.0动态权限大总结(续集)

来源:互联网 发布:淘宝虚拟对话生成器 编辑:程序博客网 时间:2024/06/05 21:18

前段时间写过一篇文章《Android 6.0动态权限大总结》,参考了网上众多博客文章的结晶。

在公司项目中应用时,还是被同事质疑了。当然了,是好事。后来在同事修改的基础上,我又进行了优化处理,现在看了是好用多了。下面分享下最后的工具类,和用法。

我就不上传代码了,直接贴给你看。


一、工具类:

DynamicPermissionCompat.java

/** * @Author: duke * @DateTime: 2017-06-08 10:51 * @Description: android 6.0 权限适配 */public class DynamicPermissionCompat {    private Activity activity;    private String[] permissions;    private int requestCode;    private OnPermissionListener listener;    private DynamicPermissionCompat(Activity activity) {        this.activity = activity;    }    /**     * 获取实例对象(非单例)     *     * @param activity     * @param permissionManager 此参数的目的是为了避免重复创建     * @return     */    public static DynamicPermissionCompat getInstance(Activity activity, DynamicPermissionCompat permissionManager) {        if (permissionManager != null) {            return permissionManager;        }        if (activity == null) {            throw new IllegalArgumentException("activity not allow null");        }        return new DynamicPermissionCompat(activity);    }    public DynamicPermissionCompat setRequestCode(int requestCode) {        this.requestCode = requestCode;        return this;    }    public DynamicPermissionCompat setPermissions(String... permissions) {        this.permissions = permissions;        return this;    }    public DynamicPermissionCompat setOnPermissionListener(OnPermissionListener listener) {        this.listener = listener;        return this;    }    /**     * 开始申请权限     */    public void start() {        if (!isNeedAdapt()) {            succeed();        } else if (permissions == null) {            failed(new ArrayList<String>());        } else {            String[] deniedPermissions = getDeniedPermissions(activity, permissions);            if (deniedPermissions.length > 0) {                //has没有授权                ActivityCompat.requestPermissions(activity, deniedPermissions, requestCode);            } else {                //全部都已经授权过了                succeed();            }        }    }    /**     * 是否需要6.0权限适配     *     * @return     */    private static boolean isNeedAdapt() {        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;    }    /**     * 向用户申请打开系统设置和浮框权限     *     * @param activity     * @return true:申请成功,false:申请失败     */    @TargetApi(Build.VERSION_CODES.M)    public static boolean adaptWriteSettingAndOverlay(Activity activity) {        if (activity == null) {            //失败,不可以继续执行            return false;        }        if (!isNeedAdapt()) {            //不需要适配,确定可以继续走            return true;        }        if (Settings.canDrawOverlays(activity) && Settings.System.canWrite(activity)) {            //已经适配,确定可以继续走            return true;        }        if (!Settings.canDrawOverlays(activity)) {            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,                    Uri.parse("package:" + activity.getPackageName()));            activity.startActivity(intent);        }        if (!Settings.System.canWrite(activity)) {            Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,                    Uri.parse("package:" + activity.getPackageName()));            activity.startActivity(intentWrite);        }        activity.finish();        android.os.Process.killProcess(android.os.Process.myPid());        //不确定是否打开权限,不可以继续执行,等待下次启动app来判断        return false;    }    /**     * 是否需要申请权限     *     * @param context     * @param permissions     * @return     */    public static boolean isNeedRequestGrantPermission(Context context, String... permissions) {        if (context == null                || permissions == null                || permissions.length == 0                || !isNeedAdapt()                || getDeniedPermissions(context, permissions).length == 0) {            return false;        }        return true;    }    /**     * 获取未授权列表     *     * @return     */    private static String[] getDeniedPermissions(Context context, String... permissions) {        if (context == null || permissions == null || permissions.length == 0) {            return new String[]{};        }        ArrayList<String> deniedList = new ArrayList<>(1);        for (String permission : permissions) {            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {                deniedList.add(permission);            }        }        return deniedList.toArray(new String[deniedList.size()]);    }    /**     * 向用户弹出解释对话框 <br/>     * *******************************************************************************     * ** 应用安装后第一次访问,直接返回false;                                     **     * ** 第一次请求权限时用户拒绝了,下一次返回 true,                             **     * ** 这时候可以显示一些为什么需要这个权限的说明;                              **     * ** 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时,返回 false;  **     * ** 设备的系统设置中禁止当前应用获取这个权限的授权,返回false;             **     * ** 注意:第二次请求权限时,才会有“不再提醒”的选项,                        **     * ** 如果用户一直拒绝,并没有选择“不再提醒”的选项,                          **     * ** 下次请求权限时,会继续有“不再提醒”的选项,并且会一直返回true            **     * *******************************************************************************     *     * @param permissions 需要提示解释的权限申请     * @return 需要提示:true,不需要:false     */    private static boolean shouldShowRationalePermissions(Activity activity, ArrayList<String> permissions) {        if (!isNeedAdapt()) {            return false;        }        if (activity == null || permissions == null || permissions.size() == 0) {            return false;        }        for (String permission : permissions) {            boolean rationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);            if (rationale) {                return true;            }        }        return false;    }    /**     * 权限授权成功     */    private void succeed() {        if (listener != null) {            listener.success(requestCode);        }    }    /**     * 用户拒绝了某个或者某些权限     *     * @param deniedList     */    private void failed(ArrayList<String> deniedList) {        /**         * shouldShowRationalePermissions(activity, deniedList):         * true:是第一次拒绝 ~ 勾选不再提醒之前。         * false:为拒绝 ~ 勾选不再提醒之后。         */        if (!shouldShowRationalePermissions(activity, deniedList)) {            if (listener != null) {                listener.alwaysDenied(requestCode, deniedList);            }        } else {            if (listener != null) {                listener.failAndTipUser(requestCode, deniedList);            }        }    }    /**     * 授权的回调     *     * @param permissions     * @param grantResults     */    public void onRequestPermissionsResult(String[] permissions, int[] grantResults) {        if (permissions.length != grantResults.length) {            failed(null);            return;        }        ArrayList<String> deniedList = new ArrayList<>(permissions.length);        for (int i = 0; i < grantResults.length; i++) {            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {                deniedList.add(permissions[i]);            }        }        deniedList.trimToSize();        if (deniedList.isEmpty()) {            succeed();        } else {            failed(deniedList);        }    }    /**     * 如果用户勾选了不再提示,打开APP设置,引导用户手动开启     */    public static void showSetting(Activity activity, int requestCode) {        if (activity == null) {            return;        }        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);        Uri uri = Uri.fromParts("package", activity.getPackageName(), null);        intent.setData(uri);        activity.startActivityForResult(intent, requestCode);    }    public static abstract class OnPermissionListener {        /**         * 全部授权成功         *         * @param requestCode         */        public abstract void success(int requestCode);        /**         * 有权限被拒绝,提示用户         *         * @param requestCode         * @param deniedPermissions 被拒绝的权限集合         */        public void failAndTipUser(int requestCode, List<String> deniedPermissions) {        }        /**         * 用户拒绝权限,并勾选了下次不再提醒         *         * @param requestCode         * @param deniedPermissions 被拒绝的权限集合         */        public void alwaysDenied(int requestCode, List<String> deniedPermissions) {        }    }}


核心东西没变,只是封装优化了。看下面的用法吧:


permissionManager = DynamicPermissionCompat.getInstance(this, permissionManager);
创建工具类对象,建议有一个Activity中创建一个对象就好。

permissionManager.setOnPermissionListener(permissionListener)        .setPermissions(permissions)        .setRequestCode(requestCode)        .start();
以构建者模式,调起授权的代码。你不用判断是否是6.0以上版本,里面的逻辑中判断了,向下兼容的。


@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    super.onRequestPermissionsResult(requestCode, permissions, grantResults);    if (permissionManager != null) {        permissionManager.onRequestPermissionsResult(permissions, grantResults);    }}
在当前Activity或者bese Activity中重写这个函数,接受回调处理。


授权回调:private DynamicPermissionCompat.OnPermissionListener permissionListener = new DynamicPermissionCompat.OnPermissionListener() {    @Override    public void success(int requestCode) {        boolean is1 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "other.apk", new File(otherFilePath));        boolean is2 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "me.apk", new File(myFilePath));//            ApkManager.installApk(ApkInstallActivity.this, new File(otherFilePath));    }    @Override    public void failAndTipUser(int requestCode, List<String> deniedPermissions) {        Toast.makeText(ApkInstallActivity.this, "用户拒绝了", Toast.LENGTH_SHORT).show();    }    @Override    public void alwaysDenied(int requestCode, List<String> deniedPermissions) {        Toast.makeText(ApkInstallActivity.this, "用户勾选了\"不再提醒\"", Toast.LENGTH_SHORT).show();    }};

瞧瞧回调函数,里面有三个方法:授权成功、用户拒绝、用户勾选了不再提醒。



+++++++++完了,就这些。与上一版本相比,是不是简单多了?+++++++++++++++++++++++++++++++++++++++++++++++++++++


下面再说说6.0权限的另一部分吧。

其实android6.0动态权限不止普通权限、动态权限两类,还有一个系统等级的权限,这个是没办法动态申请的,但是用户不打开,程序的系统设置、系统浮窗功能就是用不了,比喻修改屏幕亮度、音量等等。如果没有权限的话,你会在控制台上看到TYPE_SYSTEM_ALERT 或 WRITE_SETTINGS等权限提示。

下面提供一个封装的工具类:

SystemSettings.java

/** * @Author: duke * @DateTime: 2017-06-16 14:25 * @Description: 系统设置工具类 <br/> * 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/> * Settings.System -- WRITE_SETTINGS <br/> * Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/> * Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/> */public class SystemSettings {    public static final String TAG = SystemSettings.class.getSimpleName();    public static boolean isNeedCompatPermission() {        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;    }    /**     * 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限     *     * @param context     * @return     */    @TargetApi(Build.VERSION_CODES.M)    public static boolean hasWritePermission(Context context) {        if (context == null) {            return false;        }        if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {            return true;        }        return false;    }    /**     * 判断是否有 设置系统浮窗 权限     *     * @param context     * @return     */    @TargetApi(Build.VERSION_CODES.M)    public static boolean hasOverlaysPermission(Context context) {        if (context == null) {            return false;        }        if (!isNeedCompatPermission() || Settings.canDrawOverlays(context)) {            return true;        }        return false;    }    @TargetApi(Build.VERSION_CODES.M)    public static void guideUserWritePermission(Context context) {        if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {            return;        }        Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,                Uri.parse("package:" + context.getPackageName()));        context.startActivity(intentWrite);    }    @TargetApi(Build.VERSION_CODES.M)    public static void guideUserOverlaysPermission(Context context) {        if (context == null || !isNeedCompatPermission() || hasOverlaysPermission(context)) {            return;        }        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,                Uri.parse("package:" + context.getPackageName()));        context.startActivity(intent);    }    public static String getAndroidId(Context context) {        if (context == null) {            return "";        }        try {            return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.getAndroidId() is exception of" + e.getMessage());            e.printStackTrace();        }        return "";    }    public static int getAccelerometerRotation(Context context) {        if (context == null) {            return 0;        }        try {            return Settings.System.getInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.getAccelerometerRotation() is exception of" + e.getMessage());            e.printStackTrace();        }        return 0;    }    public static void putAccelerometerRotation(Context context, int val) {        if (context == null) {            return;        }        if (!hasWritePermission(context)) {            // TODO: 2017/6/16 需确定提示文本            Toast.makeText(context, "暂无屏幕自动切换的权限", Toast.LENGTH_LONG).show();            guideUserWritePermission(context);            return;        }        try {            Settings.System.putInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, val);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.putAccelerometerRotation() is exception of" + e.getMessage());            e.printStackTrace();        }    }    //使用的地方已被注释    public static int getScreenBrightness(Context context) {        if (context == null) {            return 0;        }        try {            return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.getScreenBrightness() is exception of" + e.getMessage());            e.printStackTrace();        }        return 0;    }    //使用的地方已被注释    public static void putScreenBrightness(Context context, int val) {        if (context == null) {            return;        }        if (!hasWritePermission(context)) {            // TODO: 2017/6/16 需确定提示文本            Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();            guideUserWritePermission(context);            return;        }        try {            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());            e.printStackTrace();        }    }    //使用的地方已被注释    public static int getScreenBrightnessMode(Context context) {        if (context == null) {            return 0;        }        try {            return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.getScreenBrightnessMode() is exception of" + e.getMessage());            e.printStackTrace();        }        return 0;    }    //使用的地方已被注释    public static void putScreenBrightnessMode(Context context, int val) {        if (context == null) {            return;        }        if (!hasWritePermission(context)) {            // TODO: 2017/6/16 需确定提示文本            Toast.makeText(context, "暂无设置系统亮度模式的权限", Toast.LENGTH_LONG).show();            guideUserWritePermission(context);            return;        }        try {            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, val);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.putScreenBrightnessMode() is exception of" + e.getMessage());            e.printStackTrace();        }    }}

拿WRITE_SETTINGS权限来,下面大概讲解下核心部分。

public static boolean isNeedCompatPermission() {        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;    }
这是6.0的权限,也需要版本判断。


/**     * 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限     *     * @param context     * @return     */    @TargetApi(Build.VERSION_CODES.M)    public static boolean hasWritePermission(Context context) {        if (context == null) {            return false;        }        if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {            return true;        }        return false;    
判断有没有修改系统设置权限的核心方法:Settings.System.canWrite(context)


如果没有呢?

@TargetApi(Build.VERSION_CODES.M)    public static void guideUserWritePermission(Context context) {        if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {            return;        }        Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,                Uri.parse("package:" + context.getPackageName()));        context.startActivity(intentWrite);    }
通过intent,引导用户去系统设置界面了。

/** * 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/> * Settings.System -- WRITE_SETTINGS <br/> * Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/> * Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/> */
WRITE_SETTINGS权限,其实就是对Settings下的System,Global,Secure的三张表修改的权限。读取不需要权限。

故,像所有Settings.xxx.putxxx()这类方法,都需要权限。建议把所有对Settings下三张表进行读写的操作都集中到这个类里面,统一进行判断处理。例如:

public static void putScreenBrightness(Context context, int val) {        if (context == null) {            return;        }        if (!hasWritePermission(context)) {            // TODO: 2017/6/16 需确定提示文本            Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();            guideUserWritePermission(context);            return;        }        try {            Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);        } catch (Exception e) {            Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());            e.printStackTrace();        }    }


还有一个系统浮窗的权限,正在整理中,后续再发布。