如需查看具體項目例子,可以去各大應(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'
非常方便,歡迎體驗 項目地址