完美解决Android 6.0+运行时权限问题

如需查看具体项目例子,可以去各大应用市场下载“萌萌鸡”app。体验功能!

android 6.0之前,我们的App需要权限,只需在manifest中申明即可,用户安装后,完全不用管权限什事情。但是android 6.0出现以后,把权限管理做了加强处理,隐私更加强,在manifest申明了,在使用到相关功能时,还需重新授权方可使用,但也不是所有的权限都需要判断,因为分了普通权限和危险权限。相信大家对权限的分类有过一点了解,这里就不详细解析了

两个建议:
1.严肃对待新权限事件,因为很多细节需要处理
2.如果你代码没支持新权限,不要设置targetSdkVersion 23 ,因为可以节省大把时间。这篇文章就不需要看了

我之前就是设置targetSdkVersion 19 面对6.0的系统,毫无压力, 后来新的SDK 出现了很多新的功能,而且网络请求也是从httpclient换成了okHttp, 必须targetSdkVersion 要大于23 ,以及为了与时俱进,换成了23,那么就要严肃对待这个问题了。

我们直接来写代码:创建一个基类,BaseActivity 需要继承 AppCompatActivity 而不能继承Activity, AppCompatActivity是在appcompat-v7中,如果app里面没有appcompat-v7 ,可以在 项目build.gradle文件中添加引用
compile 'com.android.support:appcompat-v7:23.1.1' 版本当然越高越好

Activity相信大家都会封装一个baseActivity ,在里面添加以下判断权限的方法

    /**
     * 判断是否是6.0以上的系统.很多的权限都不能自动或者提示开启. 功能可能需要多个权限,需要遍历判断
     * 
     * 萌萌鸡APP需要权限判断的地方(首页图灵聊天,个人中心AR扫描,修改头像, 设置祝福语音,视频,设置手机号码通讯录,AR,)
     *
     * @param dataPermission 需要的权限 ,数组
     * @return
     */
    public  void selfPermissionGranted(Context context, PermissionCallback runnable, String[] dataPermission) {
        MyLog.i(TAG, "selfPermissionGranted");
        this.permissionRunnable = runnable;
        int targetSdkVersion = 0;
        try {
            final PackageInfo info = context.getPackageManager().getPackageInfo(getPackageName(), 0);
            targetSdkVersion = info.applicationInfo.targetSdkVersion;//获取应用的Target版本
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        boolean resultAll = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//Build.VERSION.SDK_INT是获取当前手机版本   Build.VERSION_CODES.M为6.0系统
            MyLog.i(TAG, "Build.VERSION.SDK_INT=" + Build.VERSION.SDK_INT);
            MyLog.i(TAG, "targetSdkVersion=" + targetSdkVersion);
            MyLog.i(TAG, "Build.VERSION_CODES.M=" + Build.VERSION_CODES.M);
            //如果系统>=6.0
            if (targetSdkVersion >= Build.VERSION_CODES.M && checkPermissionGranted(context,dataPermission)) {//如果有权限
                if (permissionRunnable != null) {
                    permissionRunnable.hasPermission();
                    permissionRunnable = null;
                }
            } else {
                MyLog.i(TAG, "!checkPermissionGranted");
   //如果没有权限,请求
//判断用户是否已经拒绝过,直接去请求权限,而不管用户是否勾选了"不再询问",我们需要用户开启权限,没有权限就没有办法玩了
//      if (context.shouldShowRequestPermissionRationale(dataPermission[i])) {
//        context.requestPermissions(dataPermission, Constans.REQUEST_PERMISSION);//
//        MyLog.i(TAG, i + "+shouldShowRequestPermissionRationale=true" );//(1)只有"不在询问"才会到这一步
//    } else {
//        MyLog.i(TAG, i + "+shouldShowRequestPermissionRationale=false" );//(1)第一次原生进入 false (2)拒绝后,再次进入,还是提示false
//        isPermissionType(context, dataPermission[i]);
//    }
                requestPermissions(dataPermission, Constans.REQUEST_PERMISSION);
            }
        } else {
            MyLog.i(TAG, "Build.VERSION.SDK_INT<6.0");
            if (permissionRunnable != null) {
                permissionRunnable.hasPermission();
                permissionRunnable = null;
            }
        }
    }

/**
 *创建一个回调,方便判断权限的处理
 */
private PermissionCallback permissionRunnable;
public interface PermissionCallback {
    void hasPermission();

    void noPermission();
}

/**
 * 检测是否开启了权限  只要有一个权限没有打开,就返回false
 *
 * @param permissions
 * @return
 */
@TargetApi(Build.VERSION_CODES.M)//这里需要用23
public boolean checkPermissionGranted(Context context, String[] permissions) {
    MyLog.i(TAG, "checkPermissionGranted");
    boolean flag = true;
    for (String p : permissions) {
        MyLog.i(TAG, "permissions=" + p.toString());
        if (context.checkSelfPermission(p) != PackageManager.PERMISSION_GRANTED) {
            flag = false;
            break;
        }
    }
    return flag;
}


/**
 * 是否已经验证了权限
 *
 * @param grantResults
 * @return
 */
public boolean verifyPermissions(int[] grantResults) {
    // At least one result must be checked.
    if (grantResults.length < 1) {
        return false;
    }
    // Verify that each required permission has been granted, otherwise return false.
    for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}
/**
 * 检测权限的回调,是否开启了权限
 *
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (requestCode == Constans.REQUEST_PERMISSION) {
        for (int i = 0; i < permissions.length; i++) {//可能需要多个权限,需要遍历判断
            MyLog.i(TAG, "Permission=i=" + permissions[i] + "grantResults=" + grantResults.length);

            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {//如果点击开启权限
                if (verifyPermissions(grantResults))
                    if (permissionRunnable != null) {
                        permissionRunnable.hasPermission();
                        permissionRunnable = null;
                    }
            } else {
                MyLog.i(TAG, "Permission=" + permissions[i]);
                isPermissionType(this, permissions[i]);//我在回调的地方,弹出自定义对话框,引导用户去开启权限
                break;
            }
        }
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
 * 权限的提示,不同权限的提示不同
 *
 * @param context
 * @param permission
 */
public void isPermissionType(Activity context, String permission) {
    switch (permission) {
        case "android.permission.CAMERA"://相机权限
            showPermissionDialogs(context, context.getResources().getString(R.string.hint_camera_only), false);
            break;
        case "android.permission.WRITE_EXTERNAL_STORAGE"://写入sd卡权限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_sd_write), false);
            break;
        case "android.permission.RECORD_AUDIO"://录音权限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_record), false);
            break;
        case "android.permission.READ_CONTACTS"://通讯录权限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_contacts_only), false);
            break;
        default:
            break;
    }
}

/**
 * 开启应用权限打开提示对话框
 *
 * @param permissionHint   权限的提示文字
 * @param isFinishActivity 点击"确定""取消"的时候,是否finish当前页面
 */
public void showPermissionDialogs(final Activity context, String permissionHint, final boolean isFinishActivity) {
    final Dialog dialog = new Dialog(context, R.style.dialog);
    if (!dialog.isShowing()) {
        dialog.show();
    }
    MyLog.i(TAG, "show_dialog");
    dialog.setCanceledOnTouchOutside(false);// 设置点击屏幕Dialog不消失
    View localView = LayoutInflater.from(context).inflate(
            R.layout.dialog_hint_camera, null);
    dialog.setContentView(localView);
    TextView tvPermissionHint = (TextView) localView.findViewById(R.id.tvPermissionHint);
    TextView tvPermissionSure = (TextView) localView.findViewById(R.id.tvPermissionSure);
    TextView tvPermissionCancel = (TextView) localView.findViewById(R.id.tvPermissionCancel);
    tvPermissionHint.setText(permissionHint);
    tvPermissionSure.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if (dialog.isShowing()) {
                dialog.cancel();
            }
            Intent intent = new Intent();
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package",
                    context.getPackageName(), null);
            intent.setData(uri);
            context.startActivity(intent);
            if (isFinishActivity)  context.finish();
        }

    });
    tvPermissionCancel.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (dialog.isShowing()) {dialog.cancel();
            }
            if (isFinishActivity) context.finish();
            
        }
    });
}

Activity中,就在你需要权限判断的地方加下面一段代码

selfPermissionGranted(this,new BaseActivity.PermissionCallback() {
    @Override
    public void hasPermission() {
        openTuling();//已经开启了权限,进入处理
    }
    @Override
    public void noPermission() {
        //没有权限的处理
    }
},"android.permission.RECORD_AUDIO","android.permission.CALL_PHONE");//填写需要请求的权限,可能是多个

如果在Fragment中使用,直接在自己的BaseFragment写个方法调用此Activity的方法即可。

/**
 * Android M运行时权限请求封装
 * @param runnable 请求权限回调
 * @param permissions 请求的权限(数组类型),直接从Manifest中读取相应的值,比如Manifest.permission.WRITE_CONTACTS
 */
public void selfPermissionGranted(Context context,BaseActivity.PermissionCallback runnable, String... permissions){
    MyLog.i(TAG,"selfPermissionGranted");
    if(context!=null && context instanceof BaseActivity){
        MyLog.i(TAG,"getActivity()!=null");
        ((BaseActivity) getActivity()).selfPermissionGranted(context,runnable,permissions);
    }
}

最后,你在编写代码的时候,可能会遇到以下问题
(1).你之前用到了appcompat-v4,appcompat-v13而不是appcompat-v7 这里需要换到appcompat-v7,appcompat-v7中自定义属性命名不能是常用关键字(heght.width,color..)
(2)如果你baseActivity的父类是Activity换成了AppcompatActivity,那么主题同样需要换成的AppCompat主题
(3)如果你在fragment中调用,记得fragment要的父类不能用FragmentActivity而是用BaseActivity,因为AppCompatActivity extends FragmentActivity

最后我把代码封装了一个新的库:compile 'com.apeng:EsayPermissions:1.0.0'
非常方便,欢迎体验 项目地址

你可能感兴趣的:(完美解决Android 6.0+运行时权限问题)