版權(quán)聲明:本文來(lái)自書(shū)生依舊的簡(jiǎn)書(shū)劲藐,轉(zhuǎn)載請(qǐng)注明出處 氯窍。
一. 運(yùn)行時(shí)權(quán)限適配前
-
什么時(shí)候會(huì)觸發(fā)運(yùn)行時(shí)權(quán)限機(jī)制夺姑?
targetSdkVersion >= 23 ,運(yùn)行在 Android 6.0 及以上設(shè)備上粘舟,使用 危險(xiǎn)權(quán)限 的時(shí)候。
-
觸發(fā)了運(yùn)行時(shí)權(quán)限承匣,沒(méi)有進(jìn)行適配會(huì)怎么樣蓖乘?
應(yīng)用崩潰。
-
來(lái)不及適配怎么辦韧骗?
設(shè)置 targetSdkVersion < 23嘉抒,會(huì)和以前一樣,在應(yīng)用安裝申請(qǐng)所有的權(quán)限袍暴。值得注意的是用戶(hù)依然可以在設(shè)置里取消已授權(quán)的權(quán)限些侍,這時(shí)候應(yīng)用雖然不會(huì)崩潰,但是肯定是無(wú)法使用這個(gè)權(quán)限的政模,而且不會(huì)給用戶(hù)任何的提示岗宣。
二. 權(quán)限組
- Android 將不同的權(quán)限分組管理,任何權(quán)限都會(huì)屬于一個(gè)權(quán)限組淋样,包括正常權(quán)限和危險(xiǎn)權(quán)限耗式。
- 應(yīng)用申請(qǐng)危險(xiǎn)權(quán)限時(shí),系統(tǒng)會(huì)向用戶(hù)顯示一個(gè)對(duì)話(huà)框趁猴,描述應(yīng)用要訪(fǎng)問(wèn)的權(quán)限組刊咳,而不是不描述要申請(qǐng)的具體權(quán)限。一個(gè)權(quán)限組有一個(gè)權(quán)限申請(qǐng)成功儡司,則默認(rèn)該權(quán)限組所有權(quán)限申請(qǐng)成功娱挨,再次申請(qǐng)?jiān)摻M其他權(quán)限時(shí),系統(tǒng)將立即授予該權(quán)限捕犬,不會(huì)再顯示申請(qǐng)權(quán)限的對(duì)話(huà)框跷坝。
- 危險(xiǎn)權(quán)限共 9 組 24 個(gè)
權(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
// 檢查某個(gè)權(quán)限是否已授權(quán)
ActivityCompat.checkSelfPermission(Context context, String permission)
// permission:要檢查的權(quán)限
// 返回值是 int 類(lèi)型,PackageManager#PERMISSION_GRANTED 表示有權(quán)限碉碉,PackageManager#PERMISSION_DENIED 表示沒(méi)有權(quán)限
- requestPermissions
// 申請(qǐng)權(quán)限
ActivityCompat.requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final @IntRange(from = 0) int requesPackageManager#PERMISSION_GRANTEDtCode)
// permissions:要申請(qǐng)的權(quán)限柴钻,可以一次申請(qǐng)多個(gè)
// requestCode:請(qǐng)求碼,在申請(qǐng)權(quán)限的回調(diào)中用到
- 調(diào)用這個(gè)方法必然會(huì)走 onRequestPermissionsResult 的回調(diào)垢粮。
- onRequestPermissionsResult
// 申請(qǐng)權(quán)限的回調(diào)顿颅,在 Activity 和 Fragment 中都有
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
// requestCode:requestPermissions 的參數(shù),請(qǐng)求碼
// permissions:請(qǐng)求授權(quán)的權(quán)限,是一個(gè)數(shù)組粱腻,對(duì)應(yīng)我們前面申請(qǐng)的多個(gè)權(quán)限
// grantResults:授權(quán)結(jié)果庇配,也是一個(gè)數(shù)組,對(duì)應(yīng)上面每個(gè)權(quán)限的申請(qǐng)結(jié)果绍些,PERMISSION_GRANTED 同意捞慌,PERMISSION_DENIED 拒絕
- shouldShowRequestPermissionRationale
// 申請(qǐng)某個(gè)權(quán)限時(shí)我們是否要給用戶(hù)解釋一下
ActivityCompat.shouldShowRequestPermissionRationale(@NonNull Activity activity, @NonNull String permission)
// permission:要解釋的權(quán)限
- 如果用戶(hù)拒絕過(guò)我們的權(quán)限申請(qǐng),shouldShowRequestPermissionRationale 會(huì)返回 true柬批。此時(shí)我們最好彈出一個(gè)對(duì)話(huà)框告訴用戶(hù)啸澡,你拒絕過(guò)我的權(quán)限申請(qǐng),我申請(qǐng)這個(gè)權(quán)限是做什么用的氮帐,希望你能同意等等嗅虏。
- shouldShowRequestPermissionRationale 會(huì)返回 true 的時(shí)候,我們?cè)俅紊暾?qǐng)權(quán)限上沐,會(huì)有一個(gè) "不再提醒" 的 checkBox 皮服,當(dāng)用戶(hù)勾選上時(shí),我們?cè)俅握{(diào)用 shouldShowRequestPermissionRationale 會(huì)返回 false参咙,意思說(shuō)用戶(hù)都不想看到了龄广,就沒(méi)有必要再解釋了。
- 注意:用戶(hù)選擇 "不再提醒" 后蕴侧,再次 requestPermissions 總是會(huì)失敗择同,但是會(huì)走 onRequestPermissionsResult 的回調(diào)。
- 總結(jié):shouldShowRequestPermissionRationale 返回 false净宵,有兩種可能敲才,一是我們第一次申請(qǐng)權(quán)限的時(shí)候,二是用戶(hù)選擇了 "不再提醒"择葡。shouldShowRequestPermissionRationale 返回 true 是用戶(hù)拒絕過(guò)我們的權(quán)限申請(qǐng)但是沒(méi)有勾選 "不再提醒"紧武。
四. 權(quán)限適配最佳套路
在 AndroidManifest.xml 添加權(quán)限聲明。
使用 checkSelfPermission 檢查某個(gè)權(quán)限是否已經(jīng)申請(qǐng)刁岸。
權(quán)限未申請(qǐng)脏里,使用 requestPermissions 申請(qǐng)權(quán)限她我。
在 onRequestPermissionsResult 回調(diào)中判斷權(quán)限是否申請(qǐng)成功虹曙。
-
申請(qǐng)失敗使用 shouldShowRequestPermissionRationale 判斷用戶(hù)是否勾選了 "不再提醒"。
-
shouldShowRequestPermissionRationale 返回 fasle 勾選了的話(huà)番舆,彈出一個(gè) Dialog 引導(dǎo)用戶(hù)到設(shè)置界面授予權(quán)限酝碳,并在返回 Result 中再次判斷用戶(hù)是否同意權(quán)限,不同意的話(huà)怎給出提示恨狈。
// 到設(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 這個(gè)庫(kù) RxActivityResult.on(context) .startIntent(intent) .subscribe(activityResult -> { // 再次判斷用戶(hù)是否同意的權(quán)限疏哗,同意執(zhí)行后面的操作,不同給出提示禾怠。 checkPermission(); });
?
沒(méi)有勾選返奉,可以什么都不做贝搁,也可以彈出彈出一個(gè) Dialog 引導(dǎo)用戶(hù)到設(shè)置界面授予權(quán)限。
-
五. 需要注意的地方
- READ_PHONE_STATE芽偏、READ_EXTERNAL_STORAGE雷逆、WRITE_EXTERNAL_STORAGE 幾乎是必須的,可以放在啟動(dòng)頁(yè)申請(qǐng)污尉,用戶(hù)拒絕后引導(dǎo)至設(shè)置頁(yè)面膀哲。
- 同時(shí)申請(qǐng)多個(gè)權(quán)限時(shí),用戶(hù)可能僅僅同意某個(gè)權(quán)限被碗,在 onRequestPermissionsResult 要循環(huán)判斷每個(gè)是否申請(qǐng)成功某宪,然后進(jìn)行后續(xù)的操作。
六. 推薦幾個(gè)類(lèi)庫(kù)
兩個(gè) star 比較多的運(yùn)行時(shí)權(quán)限管理庫(kù)
-
值得一說(shuō)的是锐朴,第二個(gè)庫(kù)我在他的 issues 看到了解決了小米手機(jī)的相關(guān)問(wèn)題(具體沒(méi)有去探究)兴喂,第一個(gè)嘛,看名字也知道支持 RxJava 包颁。
還有就是上文我使用的 RxActivityResult
Thanks
Android M 新的運(yùn)行時(shí)權(quán)限開(kāi)發(fā)者需要知道的一切