https://developer.android.google.cn/guide/topics/permissions/overview#runtime
運(yùn)行時(shí)權(quán)限不是新特性,但其具體流程還未細(xì)看過,這里大致走讀下相關(guān)流程梳理下相關(guān)邏輯
要了解運(yùn)行時(shí)權(quán)限最好先看下上面開發(fā)者網(wǎng)站上的介紹
在早期的時(shí)候,一般應(yīng)用需要什么權(quán)限,只需要在AndroidManifest.xml中進(jìn)行聲明就行了,這里以讀取外部存儲(chǔ)權(quán)限為例:

以前的外部存儲(chǔ)讀取權(quán)限聲明:

但后來該權(quán)限的級別變了

明顯的,其protectionLevel從normal變?yōu)榱薲angerous,一般權(quán)限的級別為dangerous的就是運(yùn)行時(shí)權(quán)限,對于運(yùn)行時(shí)權(quán)限,若需要該權(quán)限,一般的應(yīng)用只在AndroidManifest.xml中聲明需要該權(quán)限在安裝后是默認(rèn)不會(huì)有該權(quán)限的,還需要代碼申請權(quán)限(requestPermissions),然后會(huì)有彈框,用戶選擇允許后才會(huì)擁有該權(quán)限:

而如果拒絕后,再次調(diào)用代碼申請權(quán)限,則會(huì)再次彈框:

這時(shí),如果選擇允許,則應(yīng)用擁有該權(quán)限,如果選擇“拒絕,不要再詢問”,則應(yīng)用無該權(quán)限,且再次調(diào)用代碼申請?jiān)摍?quán)限時(shí)不會(huì)彈框
開發(fā)者網(wǎng)站上推薦的申請運(yùn)行時(shí)權(quán)限的流程(https://developer.android.google.cn/training/permissions/requesting)如下:
在應(yīng)用的清單文件中,聲明應(yīng)用可能需要請求的權(quán)限。
設(shè)計(jì)應(yīng)用的用戶體驗(yàn),使應(yīng)用中的特定操作與特定運(yùn)行時(shí)權(quán)限相關(guān)聯(lián)。應(yīng)當(dāng)讓用戶知道哪些操作可能會(huì)要求他們向您的應(yīng)用授予訪問其私人數(shù)據(jù)的權(quán)限。
等待用戶調(diào)用應(yīng)用中需要訪問特定用戶私人數(shù)據(jù)的任務(wù)或操作。屆時(shí),您的應(yīng)用可以請求訪問相應(yīng)數(shù)據(jù)所需的運(yùn)行時(shí)權(quán)限。
-
檢查用戶是否已授予應(yīng)用所需的運(yùn)行時(shí)權(quán)限。如果已授權(quán),那么您的應(yīng)用可以訪問用戶私人數(shù)據(jù)。如果沒有,請繼續(xù)執(zhí)行下一步。
每次執(zhí)行需要該權(quán)限的操作時(shí),您都必須檢查自己是否具有該權(quán)限。
-
檢查您的應(yīng)用是否應(yīng)向用戶顯示理由,說明您的應(yīng)用需要用戶授予特定運(yùn)行時(shí)權(quán)限的原因。如果系統(tǒng)確定您的應(yīng)用不應(yīng)顯示理由,請繼續(xù)直接執(zhí)行下一步,無需顯示界面元素。
不過,如果系統(tǒng)確定您的應(yīng)用應(yīng)該顯示一個(gè)理由,請?jiān)诮缑嬖刂邢蛴脩麸@示理由,明確說明您的應(yīng)用試圖訪問哪些數(shù)據(jù),以及應(yīng)用獲得運(yùn)行時(shí)權(quán)限后可為用戶提供哪些好處。用戶確認(rèn)理由后,請繼續(xù)執(zhí)行下一步。
請求您的應(yīng)用訪問用戶私人數(shù)據(jù)所需的運(yùn)行時(shí)權(quán)限。系統(tǒng)會(huì)顯示運(yùn)行時(shí)權(quán)限提示,例如權(quán)限概覽頁面上顯示的提示。
檢查用戶的響應(yīng),他們可能會(huì)選擇同意或拒絕授予運(yùn)行時(shí)權(quán)限。
如果用戶向您的應(yīng)用授予權(quán)限,您就可以訪問用戶私人數(shù)據(jù)。如果用戶拒絕授予該權(quán)限,請適當(dāng)降低應(yīng)用體驗(yàn),使應(yīng)用在未獲得受該權(quán)限保護(hù)的信息時(shí)也能向用戶提供功能。
其中申請運(yùn)行時(shí)權(quán)限可仿照如下代碼
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected. In this UI,
// include a "cancel" or "no thanks" button that allows the user to
// continue using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
new String[] { Manifest.permission.REQUESTED_PERMISSION },
REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission is granted. Continue the action or workflow
// in your app.
} else {
// Explain to the user that the feature is unavailable because
// the features requires a permission that the user has denied.
// At the same time, respect the user's decision. Don't link to
// system settings in an effort to convince the user to change
// their decision.
}
return;
}
// Other 'case' lines to check for other
// permissions this app might request.
}
}
也可參考下代碼工程samples中的RuntimePermissions
其中主要代碼有
1、checkSelfPermission鑒權(quán)
2、shouldShowRequestPermissionRationale是否應(yīng)該顯示申請權(quán)限的理由(結(jié)合示例代碼和邏輯,旨在其返回true時(shí)應(yīng)用能顯示具體的權(quán)限的用處)
3、requestPermissions申請權(quán)限
1、Activity.shouldShowRequestPermissionRationale

顯然,Activity的shouldShowRequestPermissionRationale方法調(diào)用的是packageManager的,這里getPackageManager獲取的是ApplicationPackageManager的對象,查看ApplicationPackageManager的shouldShowRequestPermissionRationale方法:

顯然這里會(huì)調(diào)用到PackageManagerService的shouldShowRequestPermissionRationale方法


這里既是該方法的主要邏輯部分,其主要部分有:
1、如果已經(jīng)有對應(yīng)權(quán)限,則shouldShowRequestPermissionRationale返回false
2、如果對應(yīng)permission的flags包含fixedFlags,則返回false(這里對于一般應(yīng)用如在申請權(quán)限時(shí)用戶選擇了“拒絕,不要再詢問”后,則屬于該場景)
3、如果對應(yīng)permission的flags包含F(xiàn)LAG_PERMISSION_USER_SET則返回true(這里一般對于應(yīng)用在申請權(quán)限時(shí)用戶選擇了拒絕(不是“拒絕,不要再詢問”)后,再次申請權(quán)限時(shí)的場景),否則返回flase
1.1、申請權(quán)限用戶選擇與flags變化
這里仍以外部存儲(chǔ)讀取權(quán)限為例(android.permission.READ_EXTERNAL_STORAGE)(這里使用adb shell dumpsys package命令獲取對應(yīng)應(yīng)用的信息,如果手機(jī)root了,可以直接查看/data/system/users/0/runtime-permissions.xml文件進(jìn)行查看):
1、應(yīng)用剛安裝時(shí):

2、代碼申請權(quán)限后彈框,用戶選擇拒絕后(在之后如再次申請權(quán)限彈框,仍選擇拒絕后,其flags仍是下面的不變)(顯然,相對于剛安裝時(shí),多了USER_SET的flag):

3、代碼權(quán)限申請后彈框,用戶選擇“拒絕,不要再詢問”后(顯然,相對于剛安裝時(shí),多了USER_FIXED的flag):

4、如果在有彈框的時(shí)候,用戶選擇允許后(granted變?yōu)榱藅rue):

2、Activity.requestPermissions

從上述代碼可以看到,其實(shí)Activity.requestPermissions的主要邏輯即是啟動(dòng)了一個(gè)Activity,這里PackageManager.buildRequestPermissionsIntent邏輯如下

其實(shí)這里啟動(dòng)的是PermissionController應(yīng)用的GrantPermissionsActivity

這里簡要摘取些代碼片段介紹下大致流程
在GrantPermissionsActivity的onCreate方法中設(shè)置顯示布局:

其中mViewHandler是GrantPermissionsViewHandlerImpl的對象

查看其createView方法:

查看下其點(diǎn)擊允許按鈕(mAllowButton)的大致邏輯,查看GrantPermissionsViewHandlerImpl中onClick邏輯:

顯然,在點(diǎn)擊允許的時(shí)候,其主要是回調(diào)GrantPermissionsActivity的onPermissionGrantResult方法:

繼續(xù)查看GrantPermissionsActivity的onPermissionGrantResultSingleState方法

這里選擇允許時(shí),主要邏輯是調(diào)用上面groupState.mGroup.grantRuntimePermissions方法,其中mGroup是AppPermissionGroup對象
在AppPermissionGroup的grantRuntimePermissions中會(huì)先對對應(yīng)權(quán)限持有的permission對象進(jìn)行對應(yīng)的賦值,然后其會(huì)調(diào)用persistChanges方法進(jìn)行權(quán)限的處理,如賦予權(quán)限、更新flags等

另外在用戶選擇了“拒絕,不要再詢問”后,再次調(diào)用requestPermissions方法時(shí),仍是去啟動(dòng)上面GrantPermissionsActivity,只是在其onCreate方法中會(huì)調(diào)用setResultAndFinish方法退出,所以看起來沒有反應(yīng)
