Android7.0运行时权限的解决方案

运行时权限

Android6.0以后Google不再允许开发者直接或许应用的权限,需要在用户知情的情况下授予权限,即需要用到权限的时候弹出一个权限提示框。

这里写图片描述

接下来我们将使用三种方式,完成运行时权限的申请:

  1. 自己编写代码实现,完全搞懂运行时权限的申请。
  2. 使用Google官方简化权限申请库easypermissions实现。
  3. 使用结合RxJava的权限库RxPermissions实现。

为了避免用户没有留意而拒绝了应用的关键权限,我们必须对用户授权时的各种状态进行详细的分析。

用户在授予权限时存在三种状态:

  1. 允许—授权成功,授权结束。
  2. 拒绝—授权失败,下次申请仍会弹出提示框。如果权限是程序必须拥有的,则需要再次申请。
  3. 拒绝&不再询问—拒绝一次后,下次会有该提示。若选择拒绝并且不再询问,下次申请则不会弹出提示框。如果权限是程序必须拥有的,则需要用户到应用管理中手动设置。

manifest

首先在manifest中添加如下4个权限声明,方便之后申请权限。

如果不添加,使用代码申请权限,申请的结果就是默认:拒绝&不再询问。

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- 录音 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

申请运行时权限的基本流程

    /**
         * 1.判断系统版本
         * 2.调用 requestPermissions(权限集合,requestCode);
         */
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(mPermissions,0x001);
        }

     /**
     *  3.申请权限的结果回调
     * @param requestCode
     * @param mPermissions
     * @param grantResults
     */
     @Override
    public void onRequestPermissionsResult(int requestCode, String[] mPermissions, int[] grantResults) {

        super.onRequestPermissionsResult(requestCode, mPermissions, grantResults);
    }

自己编写代码实现

权限状态监听接口

public interface PermissionInterface {

    /**
     * 可设置请求权限请求码
     */
    int getPermissionsRequestCode();

    /**
     * 设置需要请求的权限
     */
    String[] getPermissions();

    /**
     * 请求权限成功回调
     */
    void requestPermissionsSuccess();

    /**
     * 请求权限失败回调
     */
    void requestPermissionsFail();
}

2.申请权限,对申请权限的结果进行分析,并监听权限申请的状态

public class PermissionHelper {

    private Activity mActivity;
    private PermissionInterface mPermissionInterface;

    public PermissionHelper(Activity activity, PermissionInterface permissionInterface) {
        mActivity = activity;
        mPermissionInterface = permissionInterface;
    }

    /**
     * 开始请求权限。
     * 方法内部已经对Android M 或以上版本进行了判断,外部使用不再需要重复判断。
     * 如果设备还不是M或以上版本,则也会回调到requestPermissionsSuccess方法。
     */
    public void requestPermissions() {
        String[] deniedPermissions = PermissionUtil.getDeniedPermissions(mActivity, mPermissionInterface.getPermissions());
        if (deniedPermissions != null && deniedPermissions.length > 0) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //Android 6.0及以后才需要运行时获取权限
                mActivity.requestPermissions(deniedPermissions, mPermissionInterface.getPermissionsRequestCode());
            }
        } else {
            mPermissionInterface.requestPermissionsSuccess();
        }
    }

    /**
     * 在Activity中的onRequestPermissionsResult中调用
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     * @return true 代表对该requestCode感兴趣,并已经处理掉了。false 对该requestCode不感兴趣,不处理。
     */
    public boolean requestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == mPermissionInterface.getPermissionsRequestCode()) {
            boolean isAllGranted = true;//是否全部权限已授权
            for (int result : grantResults) {
                if (result == PackageManager.PERMISSION_DENIED) {
                    isAllGranted = false;
                    break;
                }
            }
            if (isAllGranted) {
                //已全部授权
                mPermissionInterface.requestPermissionsSuccess();
            } else {
                //权限有缺失
                mPermissionInterface.requestPermissionsFail();
            }
            return true;
        }
        return false;
    }

}

3.检查权限的方法

public class PermissionUtil {

    /**
     * 检查所需权限中缺少的权限
     * 防止权限重复获取
     *
     * @param context
     * @param permissions
     * @return 返回缺少的权限,null 意味着没有缺少权限
     */
    public static String[] getDeniedPermissions(Context context, String[] permissions) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ArrayList<String> deniedPermissionList = new ArrayList<>();
            for (String permission : permissions) {
                if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                    deniedPermissionList.add(permission);
                }
            }
            int size = deniedPermissionList.size();
            if (size > 0) {
                return deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
            }
        }
        return null;
    }

    /**
     * 提醒用户手动设置权限的对话框
     *
     * @param activity
     * @param text
     */
    public static void PermissionDialog(final Activity activity, String text) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setCancelable(false);
        builder.setMessage(text);
        builder.setNegativeButton("算了", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                activity.finish();//没有权限,关闭当前页面
            }
        });
        builder.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //去应用管理界面手动给予权限
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + activity.getPackageName()));
                activity.startActivity(intent);
            }
        });
        builder.create().show();
    }

    /**
     * 权限的提示文字
     * 可以根据应用需求自行更改
     *
     * @param perms
     * @return
     */
    public static String permissionText(String[] perms) {
        StringBuilder sb = new StringBuilder();
        for (String s : perms) {
            if (s.equals(Manifest.permission.CAMERA)) {
                sb.append("相机权限(用于拍照,视频聊天);\n");
            } else if (s.equals(Manifest.permission.RECORD_AUDIO)) {
                sb.append("麦克风权限(用于发语音,语音及视频聊天);\n");
            } else if (s.equals(Manifest.permission.READ_EXTERNAL_STORAGE)){
                sb.append("读取权限(用于读取数据);\n");
            }else if (s.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                sb.append("存储(用于存储必要信息,缓存数据);\n");
            }
        }
        return "程序运行需要如下权限:\n" + sb.toString();
    }
}

4.在Activity中使用

public class MainActivity extends AppCompatActivity implements PermissionInterface {
    //要申请的权限
    private String[] mPermissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
    private PermissionHelper permissionHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化
        permissionHelper = new PermissionHelper(this, this);
        //申请权限
        permissionHelper.requestPermissions();    
    }

    @Override
    public int getPermissionsRequestCode() {
        return 0;
    }

    @Override
    public String[] getPermissions() {
        return mPermissions;
    }

    @Override
    public void requestPermissionsSuccess() {
        //do something
    }

    @Override
    public void requestPermissionsFail() {
        StringBuilder sb = new StringBuilder();
        mPermissions = PermissionUtil.getDeniedPermissions(this, mPermissions);
        for (String s : mPermissions) {
            if (s.equals(Manifest.permission.CAMERA)) {
                sb.append("相机权限(用于拍照,视频聊天);\n");
            } else if (s.equals(Manifest.permission.RECORD_AUDIO)) {
                sb.append("麦克风权限(用于发语音,语音及视频聊天);\n");
            } else if (s.equals(Manifest.permission.READ_EXTERNAL_STORAGE) || s.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                sb.append("存储,读取权限(用于存储必要信息,缓存数据);\n");
            }
        }
        PermissionUtil.PermissionDialog(this, "程序运行需要如下权限:\n" + sb.toString() + "请在应用权限管理进行设置!");
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] mPermissions, int[] grantResults) {
        if (permissionHelper.requestPermissionsResult(requestCode, mPermissions, grantResults)) {
            //权限请求结果,并已经处理了该回调
            return;
        }
        super.onRequestPermissionsResult(requestCode, mPermissions, grantResults);
    }
}

5.总结

  1. 分解了权限申请的各个过程,并对申请中权限的状态进行分析监听回调,方便了处理的过程。
  2. 不足之处是,因为:
  /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has been granted to the given package.
     */
    public static final int PERMISSION_GRANTED = 0;

    /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has not been granted to the given package.
     */
    public static final int PERMISSION_DENIED = -1;

所以无法分辨拒绝拒绝&不再询问两种状态,只能统一算作授权失败统一处理。

easypermissions

Google自己都觉得申请运行时权限太过于麻烦,于是自己写了easypermissions来解决这个问题。
该开源库的GitHub地址为:https://github.com/googlesamples/easypermissions

1.添加库

  implementation 'pub.devrel:easypermissions:1.2.0'//简化权限库

2.使用


/**
 * EasyPermissions.PermissionCallbacks 权限获取状态的监听
 * EasyPermissions.RationaleCallbacks 权限获取失败后弹出的对话框的[取消,确认]按钮监听
 */
public class Main2Activity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks {
    //要申请的权限
    private String[] mPermissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
    public static final int CODE = 0x001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        //检查权限,防止重复获取
        mPermissions = PermissionUtil.getDeniedPermissions(this, mPermissions);
        if (mPermissions.length > 0) {
             /**
             * 1.上下文
             * 2.权限失败后弹出对话框的内容
             * 3.requestCode
             * 4.要申请的权限
             */
            EasyPermissions.requestPermissions(this, PermissionUtil.permissionText(mPermissions), CODE, mPermissions);
        }
    }

    //所有的权限申请成功的回调
    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        //do something
    }

    //权限获取失败的回调
    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        //存在被永久拒绝(拒绝&不再询问)的权限
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            mPermissions = PermissionUtil.getDeniedPermissions(this, mPermissions);
            PermissionUtil.PermissionDialog(this, PermissionUtil.permissionText(mPermissions) + "请在应用权限管理进行设置!");
        } else {
            EasyPermissions.requestPermissions(this, PermissionUtil.permissionText(mPermissions), CODE, mPermissions);
        }
    }

    //权限被拒绝后的显示提示对话框,点击确认的回调
    @Override
    public void onRationaleAccepted(int requestCode) {
        //会自动再次获取没有申请成功的权限
        //do something
    }

    //权限被拒绝后的显示提示对话框,点击取消的回调
    @Override
    public void onRationaleDenied(int requestCode) {
        //什么都不会做
        //do something
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //将结果传入EasyPermissions中
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }
}

3.总结

  1. 针对Activity进行了封装,使用场景更加丰富。
  2. 添加了拒绝后的弹出对话框。
  3. 注释已经写的很详细,更多请看GitHub文档https://github.com/googlesamples/easypermissions

RxPermissions

结合RxJava的运行时权限请求库,必须结合RxJava使用,使用及其简单。
GitHub:https://github.com/tbruyelle/RxPermissions

1.添加

 implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'
 implementation 'io.reactivex.rxjava2:rxjava:2.1.9'

2.使用

RxPermissions rxPermissions = new RxPermissions(MainActivity.this);
                rxPermissions.requestEach(mPermissions).subscribe(new Consumer<Permission>() {
                    @Override
                    public void accept(Permission permission) {
                        Log.i(TAG, "accept: " + permission.toString());
                        if (permission.granted) {
                            //权限获取成功
                        } else if (permission.shouldShowRequestPermissionRationale) {
                            //权限获取失败,但是没有永久拒绝
                        } else {
                            //权限获取失败,而且被永久拒绝
                        }
                    }
                });

3.总结

  1. 使用及其简单,方便,代码少。
  2. 只会申请没有获取的权限,无须筛选就不会重复获取权限。
  3. 必须同时添加RxJava库,必须先学习使用RxJava,使用成本偏高。
  4. 可以直接绑定点击事件,使用场景更加丰富。
  5. 更多请看GitHub文档https://github.com/tbruyelle/RxPermissions

代码

GitHub: https://github.com/Demo-DeMon/PermissionDemo

猜你喜欢

转载自blog.csdn.net/demonliuhui/article/details/80777486