版權(quán)聲明:本文來自書生依舊的簡書,轉(zhuǎn)載請注明出處 。
一. 運行時權(quán)限適配前
-
什么時候會觸發(fā)運行時權(quán)限機制?
targetSdkVersion >= 23 ,運行在 Android 6.0 及以上設(shè)備上,使用 危險權(quán)限 的時候。
-
觸發(fā)了運行時權(quán)限,沒有進行適配會怎么樣?
應用崩潰。
-
來不及適配怎么辦?
設(shè)置 targetSdkVersion < 23,會和以前一樣,在應用安裝申請所有的權(quán)限。值得注意的是用戶依然可以在設(shè)置里取消已授權(quán)的權(quán)限,這時候應用雖然不會崩潰,但是肯定是無法使用這個權(quán)限的,而且不會給用戶任何的提示。
二. 權(quán)限組
- Android 將不同的權(quán)限分組管理,任何權(quán)限都會屬于一個權(quán)限組,包括正常權(quán)限和危險權(quán)限。
- 應用申請危險權(quán)限時,系統(tǒng)會向用戶顯示一個對話框,描述應用要訪問的權(quán)限組,而不是不描述要申請的具體權(quán)限。一個權(quán)限組有一個權(quán)限申請成功,則默認該權(quán)限組所有權(quán)限申請成功,再次申請該組其他權(quán)限時,系統(tǒng)將立即授予該權(quán)限,不會再顯示申請權(quán)限的對話框。
- 危險權(quán)限共 9 組 24 個
| 權(quán)限組 | 權(quán)限 |
|---|---|
| CALENDAR |
READ_CALENDAR WRITE_CALENDAR
|
| CAMERA | CAMERA |
| CONTACTS |
READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS
|
| LOCATION |
ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION
|
| MICROPHONE | RECORD_AUDIO |
| PHONE |
READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS
|
| SENSORS | BODY_SENSORS |
| SMS |
SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS
|
| STORAGE |
READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE
|
三. API 詳解
- checkSelfPermission
// 檢查某個權(quán)限是否已授權(quán)
ActivityCompat.checkSelfPermission(Context context, String permission)
// permission:要檢查的權(quán)限
// 返回值是 int 類型,PackageManager#PERMISSION_GRANTED 表示有權(quán)限,PackageManager#PERMISSION_DENIED 表示沒有權(quán)限
- requestPermissions
// 申請權(quán)限
ActivityCompat.requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final @IntRange(from = 0) int requesPackageManager#PERMISSION_GRANTEDtCode)
// permissions:要申請的權(quán)限,可以一次申請多個
// requestCode:請求碼,在申請權(quán)限的回調(diào)中用到
- 調(diào)用這個方法必然會走 onRequestPermissionsResult 的回調(diào)。
- onRequestPermissionsResult
// 申請權(quán)限的回調(diào),在 Activity 和 Fragment 中都有
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
// requestCode:requestPermissions 的參數(shù),請求碼
// permissions:請求授權(quán)的權(quán)限,是一個數(shù)組,對應我們前面申請的多個權(quán)限
// grantResults:授權(quán)結(jié)果,也是一個數(shù)組,對應上面每個權(quán)限的申請結(jié)果,PERMISSION_GRANTED 同意,PERMISSION_DENIED 拒絕
- shouldShowRequestPermissionRationale
// 申請某個權(quán)限時我們是否要給用戶解釋一下
ActivityCompat.shouldShowRequestPermissionRationale(@NonNull Activity activity, @NonNull String permission)
// permission:要解釋的權(quán)限
- 如果用戶拒絕過我們的權(quán)限申請,shouldShowRequestPermissionRationale 會返回 true。此時我們最好彈出一個對話框告訴用戶,你拒絕過我的權(quán)限申請,我申請這個權(quán)限是做什么用的,希望你能同意等等。
- shouldShowRequestPermissionRationale 會返回 true 的時候,我們再次申請權(quán)限,會有一個 "不再提醒" 的 checkBox ,當用戶勾選上時,我們再次調(diào)用 shouldShowRequestPermissionRationale 會返回 false,意思說用戶都不想看到了,就沒有必要再解釋了。
- 注意:用戶選擇 "不再提醒" 后,再次 requestPermissions 總是會失敗,但是會走 onRequestPermissionsResult 的回調(diào)。
- 總結(jié):shouldShowRequestPermissionRationale 返回 false,有兩種可能,一是我們第一次申請權(quán)限的時候,二是用戶選擇了 "不再提醒"。shouldShowRequestPermissionRationale 返回 true 是用戶拒絕過我們的權(quán)限申請但是沒有勾選 "不再提醒"。
四. 權(quán)限適配最佳套路
在 AndroidManifest.xml 添加權(quán)限聲明。
使用 checkSelfPermission 檢查某個權(quán)限是否已經(jīng)申請。
權(quán)限未申請,使用 requestPermissions 申請權(quán)限。
在 onRequestPermissionsResult 回調(diào)中判斷權(quán)限是否申請成功。
-
申請失敗使用 shouldShowRequestPermissionRationale 判斷用戶是否勾選了 "不再提醒"。
-
shouldShowRequestPermissionRationale 返回 fasle 勾選了的話,彈出一個 Dialog 引導用戶到設(shè)置界面授予權(quán)限,并在返回 Result 中再次判斷用戶是否同意權(quán)限,不同意的話怎給出提示。
// 到設(shè)置界面授予權(quán)限 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package",getPackageName(), null); intent.setData(uri); // startActivityForResult(intent); // 我這里使用的是 RxActivityResult 這個庫 RxActivityResult.on(context) .startIntent(intent) .subscribe(activityResult -> { // 再次判斷用戶是否同意的權(quán)限,同意執(zhí)行后面的操作,不同給出提示。 checkPermission(); });?
沒有勾選,可以什么都不做,也可以彈出彈出一個 Dialog 引導用戶到設(shè)置界面授予權(quán)限。
-
五. 需要注意的地方
- READ_PHONE_STATE、READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE 幾乎是必須的,可以放在啟動頁申請,用戶拒絕后引導至設(shè)置頁面。
- 同時申請多個權(quán)限時,用戶可能僅僅同意某個權(quán)限,在 onRequestPermissionsResult 要循環(huán)判斷每個是否申請成功,然后進行后續(xù)的操作。
六. 推薦幾個類庫
兩個 star 比較多的運行時權(quán)限管理庫
-
值得一說的是,第二個庫我在他的 issues 看到了解決了小米手機的相關(guān)問題(具體沒有去探究),第一個嘛,看名字也知道支持 RxJava 。
還有就是上文我使用的 RxActivityResult