完美解決Android 6.0+運行時權(quán)限問題

如需查看具體項目例子,可以去各大應(yīng)用市場下載“萌萌雞”app。體驗功能!

android 6.0之前,我們的App需要權(quán)限,只需在manifest中申明即可,用戶安裝后,完全不用管權(quán)限什事情。但是android 6.0出現(xiàn)以后,把權(quán)限管理做了加強處理,隱私更加強,在manifest申明了,在使用到相關(guān)功能時,還需重新授權(quán)方可使用,但也不是所有的權(quán)限都需要判斷,因為分了普通權(quán)限和危險權(quán)限。相信大家對權(quán)限的分類有過一點了解,這里就不詳細(xì)解析了

兩個建議:
1.嚴(yán)肅對待新權(quán)限事件,因為很多細(xì)節(jié)需要處理
2.如果你代碼沒支持新權(quán)限,不要設(shè)置targetSdkVersion 23 ,因為可以節(jié)省大把時間。這篇文章就不需要看了

我之前就是設(shè)置targetSdkVersion 19 面對6.0的系統(tǒng),毫無壓力, 后來新的SDK 出現(xiàn)了很多新的功能,而且網(wǎng)絡(luò)請求也是從httpclient換成了okHttp, 必須targetSdkVersion 要大于23 ,以及為了與時俱進,換成了23,那么就要嚴(yán)肅對待這個問題了。

我們直接來寫代碼:創(chuàng)建一個基類,BaseActivity 需要繼承 AppCompatActivity 而不能繼承Activity, AppCompatActivity是在appcompat-v7中,如果app里面沒有appcompat-v7 ,可以在 項目build.gradle文件中添加引用
compile 'com.android.support:appcompat-v7:23.1.1' 版本當(dāng)然越高越好

Activity相信大家都會封裝一個baseActivity ,在里面添加以下判斷權(quán)限的方法

    /**
     * 判斷是否是6.0以上的系統(tǒng).很多的權(quán)限都不能自動或者提示開啟. 功能可能需要多個權(quán)限,需要遍歷判斷
     * 
     * 萌萌雞APP需要權(quán)限判斷的地方(首頁圖靈聊天,個人中心AR掃描,修改頭像, 設(shè)置祝福語音,視頻,設(shè)置手機號碼通訊錄,AR,)
     *
     * @param dataPermission 需要的權(quán)限 ,數(shù)組
     * @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;//獲取應(yīng)用的Target版本
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        boolean resultAll = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//Build.VERSION.SDK_INT是獲取當(dāng)前手機版本   Build.VERSION_CODES.M為6.0系統(tǒng)
            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);
            //如果系統(tǒng)>=6.0
            if (targetSdkVersion >= Build.VERSION_CODES.M && checkPermissionGranted(context,dataPermission)) {//如果有權(quán)限
                if (permissionRunnable != null) {
                    permissionRunnable.hasPermission();
                    permissionRunnable = null;
                }
            } else {
                MyLog.i(TAG, "!checkPermissionGranted");
   //如果沒有權(quán)限,請求
//判斷用戶是否已經(jīng)拒絕過,直接去請求權(quán)限,而不管用戶是否勾選了"不再詢問",我們需要用戶開啟權(quán)限,沒有權(quán)限就沒有辦法玩了
//      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;
            }
        }
    }

/**
 *創(chuàng)建一個回調(diào),方便判斷權(quán)限的處理
 */
private PermissionCallback permissionRunnable;
public interface PermissionCallback {
    void hasPermission();

    void noPermission();
}

/**
 * 檢測是否開啟了權(quán)限  只要有一個權(quán)限沒有打開,就返回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;
}


/**
 * 是否已經(jīng)驗證了權(quán)限
 *
 * @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;
}
/**
 * 檢測權(quán)限的回調(diào),是否開啟了權(quán)限
 *
 * @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++) {//可能需要多個權(quán)限,需要遍歷判斷
            MyLog.i(TAG, "Permission=i=" + permissions[i] + "grantResults=" + grantResults.length);

            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {//如果點擊開啟權(quán)限
                if (verifyPermissions(grantResults))
                    if (permissionRunnable != null) {
                        permissionRunnable.hasPermission();
                        permissionRunnable = null;
                    }
            } else {
                MyLog.i(TAG, "Permission=" + permissions[i]);
                isPermissionType(this, permissions[i]);//我在回調(diào)的地方,彈出自定義對話框,引導(dǎo)用戶去開啟權(quán)限
                break;
            }
        }
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
 * 權(quán)限的提示,不同權(quán)限的提示不同
 *
 * @param context
 * @param permission
 */
public void isPermissionType(Activity context, String permission) {
    switch (permission) {
        case "android.permission.CAMERA"://相機權(quán)限
            showPermissionDialogs(context, context.getResources().getString(R.string.hint_camera_only), false);
            break;
        case "android.permission.WRITE_EXTERNAL_STORAGE"://寫入sd卡權(quán)限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_sd_write), false);
            break;
        case "android.permission.RECORD_AUDIO"://錄音權(quán)限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_record), false);
            break;
        case "android.permission.READ_CONTACTS"://通訊錄權(quán)限
            Tools.showPermissionDialogs(context, context.getResources().getString(R.string.hint_contacts_only), false);
            break;
        default:
            break;
    }
}

/**
 * 開啟應(yīng)用權(quán)限打開提示對話框
 *
 * @param permissionHint   權(quán)限的提示文字
 * @param isFinishActivity 點擊"確定""取消"的時候,是否finish當(dāng)前頁面
 */
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);// 設(shè)置點擊屏幕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中,就在你需要權(quán)限判斷的地方加下面一段代碼

selfPermissionGranted(this,new BaseActivity.PermissionCallback() {
    @Override
    public void hasPermission() {
        openTuling();//已經(jīng)開啟了權(quán)限,進入處理
    }
    @Override
    public void noPermission() {
        //沒有權(quán)限的處理
    }
},"android.permission.RECORD_AUDIO","android.permission.CALL_PHONE");//填寫需要請求的權(quán)限,可能是多個

如果在Fragment中使用,直接在自己的BaseFragment寫個方法調(diào)用此Activity的方法即可。

/**
 * Android M運行時權(quán)限請求封裝
 * @param runnable 請求權(quán)限回調(diào)
 * @param permissions 請求的權(quán)限(數(shù)組類型),直接從Manifest中讀取相應(yīng)的值,比如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中自定義屬性命名不能是常用關(guān)鍵字(heght.width,color..)
(2)如果你baseActivity的父類是Activity換成了AppcompatActivity,那么主題同樣需要換成的AppCompat主題
(3)如果你在fragment中調(diào)用,記得fragment要的父類不能用FragmentActivity而是用BaseActivity,因為AppCompatActivity extends FragmentActivity

最后我把代碼封裝了一個新的庫:compile 'com.apeng:EsayPermissions:1.0.0'
非常方便,歡迎體驗 項目地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容