前言
閱讀前說明:
- 所有系統(tǒng)均為Android原生系統(tǒng),其他國(guó)產(chǎn)ROM最后再討論。
- 所有的申請(qǐng)都為主動(dòng)觸發(fā),即主動(dòng)點(diǎn)擊申請(qǐng)按鈕。
- 申請(qǐng)時(shí),默認(rèn)是沒有權(quán)限的。
- 流程圖中長(zhǎng)方形中為方法名。
流程圖
申請(qǐng)單個(gè)權(quán)限流程圖

申請(qǐng)權(quán)限組流程圖

申請(qǐng)混合權(quán)限(單個(gè)權(quán)限+權(quán)限組)流程圖
具體流程=單個(gè)權(quán)限流程圖+權(quán)限組流程圖
代碼中有例子,就不詳細(xì)說明了。
EasyPermissions使用
- 依賴
dependencies {
implementation 'pub.devrel:easypermissions:1.2.0'
}
- 檢查權(quán)限并申請(qǐng)權(quán)限
public static final int PERMISSION_STORAGE_CODE = 10001;
public static final String PERMISSION_STORAGE_MSG = "此app需要獲取SD卡讀取權(quán)限";
public static final String[] PERMISSION_STORAGE = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};
public static boolean hasPermissions(Context context, String... permissions) {
return EasyPermissions.hasPermissions(context, permissions);
}
/**
* 是否有SD卡權(quán)限
*
* @param context
* @return
*/
public static boolean hasStoragePermission(Context context) {
return hasPermissions(context, PERMISSION_STORAGE);
}
@AfterPermissionGranted(PERMISSION_STORAGE_CODE)
public void initSimple() {
if (hasStoragePermission(context)) {
//有權(quán)限
} else {
//申請(qǐng)權(quán)限
EasyPermissions.requestPermissions(context, PERMISSION_STORAGE_MSG, PERMISSION_STORAGE_CODE, PERMISSION_STORAGE);
}
}
- 實(shí)現(xiàn)
EasyPermissions.PermissionCallbacks接口,直接處理權(quán)限是否成功申請(qǐng)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//成功
@Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Some permissions have been granted
// ...
}
//失敗
@Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// Some permissions have been denied
// ...
}
Dailog說明
- RationaleDialog:申請(qǐng)權(quán)限的說明。
[圖片上傳失敗...(image-da33d8-1530149961979)]
EasyPermissions.requestPermissions(context, PERMISSION_STORAGE_MSG, PERMISSION_STORAGE_CODE, PERMISSION_STORAGE);
或者
EasyPermissions.requestPermissions(
new PermissionRequest.Builder(this, RC_CAMERA_AND_LOCATION, perms)
.setRationale(R.string.camera_and_location_rationale)
.setPositiveButtonText(R.string.rationale_ask_ok)
.setNegativeButtonText(R.string.rationale_ask_cancel)
.setTheme(R.style.my_fancy_style)
.build());
方法回調(diào)實(shí)現(xiàn)EasyPermissions.RationaleCallbacks接口:
/**
* dialog提醒點(diǎn)擊“確定”
*
* @param requestCode
*/
@Override
public void onRationaleAccepted(int requestCode) {
}
/**
* dialog提醒點(diǎn)擊“取消”
*
* @param requestCode
*/
@Override
public void onRationaleDenied(int requestCode) {
}
詳細(xì)使用參考:easypermissions
- AppSettingsDialog:跳轉(zhuǎn)setting頁(yè),申請(qǐng)被拒絕時(shí)使用。

/**
* 拒絕權(quán)限
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this)
.setTitle("提醒")
.setRationale("此app需要這些權(quán)限才能正常使用")
.build()
.show();
}
}
方法回調(diào)
/**
/**
* 設(shè)置頁(yè)面回調(diào)
*
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) {
if (PermissionUtils.hasStoragePermission(context)){
//有權(quán)限
}else{
//沒有權(quán)限
}
}
}
國(guó)產(chǎn)ROM適配
國(guó)產(chǎn)ROM問題一直是最多的,申請(qǐng)權(quán)限的彈窗不按Android原生來,有時(shí)候允許權(quán)限了,回調(diào)卻是走失敗。
如果想適配國(guó)產(chǎn)ROM,我推薦 AndPermission
AndPermission概述
AndPermission可以簡(jiǎn)化在Android上請(qǐng)求權(quán)限的流程。
- 在Android6.0或者更高版本的系統(tǒng)中請(qǐng)求運(yùn)行時(shí)權(quán)限。
- 在Android7.0或者更高版本的系統(tǒng)中分享私有文件。
- 在Android8.0或者更高版本的系統(tǒng)中安裝Apk文件。
請(qǐng)求流程
為了兼容一些特殊中國(guó)產(chǎn)手機(jī),AndPermission請(qǐng)求權(quán)限的流程是這樣的:
發(fā)起請(qǐng)求
│
調(diào)用SDK Api判斷是否有權(quán)限
│
┌─────────────┴─────────────┐
有權(quán)限 沒權(quán)限
│ │
│ 使用SDK Api請(qǐng)求權(quán)限
│ │
│ 用戶授權(quán)或者拒絕
└─────────────┬─────────────┘
執(zhí)行權(quán)限相關(guān)代碼
┌───────┴───────┐
正常 異常
│ │
onGranted() onDenied()
┌──────┴──────┐ │
正常 異常───────┘
申請(qǐng)流程的釋義
可以看出AndPermission的申請(qǐng)流程和標(biāo)準(zhǔn)的申請(qǐng)流程大相徑庭,這樣做主要是因?yàn)橐韵聨c(diǎn)原因。
- 兼容Android5.0的系統(tǒng)判斷是否有權(quán)限。
- 部分設(shè)備上使用SDK的Api判斷是否有權(quán)限時(shí),無論是否有權(quán)限都返回true。
- 部分設(shè)備上無論用戶點(diǎn)擊同意還是拒絕都返回true。
- 部分設(shè)備在申請(qǐng)權(quán)限時(shí)并不會(huì)彈出授權(quán)Dialog,而是在執(zhí)行權(quán)限相關(guān)代碼時(shí)才會(huì)彈出授權(quán)Dialog。
特特特別注意:
因?yàn)椴糠謾?quán)限(例如發(fā)送短信、打電話、接聽/掛斷電話等),AndPermission不能執(zhí)行權(quán)限相關(guān)代碼做測(cè)試,所以對(duì)于這一類權(quán)限在執(zhí)行權(quán)限相關(guān)代碼步驟僅僅使用了AppOpsManager做檢測(cè)。因?yàn)闃O少部分國(guó)產(chǎn)機(jī)總是返回true,因此直接回調(diào)到onGranted()中讓開發(fā)者執(zhí)行相關(guān)代碼來輔助AndPermission的整個(gè)流程,如果onGranted()方法發(fā)生異常,那么則認(rèn)為是沒有權(quán)限,所以重新回調(diào)到onDenied()方法中,如果onGranted()方法正常執(zhí)行那么則認(rèn)為有權(quán)限。這樣一來剛好是一個(gè)完整的流程,也可以最大限度的兼容到更多異常的手機(jī)。
回調(diào)onGranted()時(shí)AndPermission對(duì)onGranted()方法做了try catch。這樣做可以保證兩點(diǎn),第一點(diǎn)是防止部分手機(jī)返回錯(cuò)誤狀態(tài)時(shí)回調(diào)了onGranted()后的崩潰,第二點(diǎn)是防止部分手機(jī)需要真正執(zhí)行權(quán)限相關(guān)代碼時(shí)觸發(fā)授權(quán)Dialog后被用戶拒絕后的崩潰。而這兩點(diǎn)都是沒有權(quán)限,因此會(huì)重新回調(diào)到onDenied()中讓開發(fā)者處理沒權(quán)限的情況。