運(yùn)行時(shí)權(quán)限

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)限為例:

image-20210915221106057.png

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


image-20210915221452179.png

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


image-20210915221539087.png

明顯的,其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)限:


image-20210915222930197.png

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


image-20210915223410814.png

這時(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)如下:

  1. 在應(yīng)用的清單文件中,聲明應(yīng)用可能需要請求的權(quán)限。

  2. 設(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)限。

  3. 等待用戶調(diào)用應(yīng)用中需要訪問特定用戶私人數(shù)據(jù)的任務(wù)或操作。屆時(shí),您的應(yīng)用可以請求訪問相應(yīng)數(shù)據(jù)所需的運(yùn)行時(shí)權(quán)限。

  4. 檢查用戶是否已授予應(yīng)用所需的運(yùn)行時(shí)權(quán)限。如果已授權(quán),那么您的應(yīng)用可以訪問用戶私人數(shù)據(jù)。如果沒有,請繼續(xù)執(zhí)行下一步。

    每次執(zhí)行需要該權(quán)限的操作時(shí),您都必須檢查自己是否具有該權(quán)限。

  5. 檢查您的應(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í)行下一步。

  6. 請求您的應(yīng)用訪問用戶私人數(shù)據(jù)所需的運(yùn)行時(shí)權(quán)限。系統(tǒng)會(huì)顯示運(yùn)行時(shí)權(quán)限提示,例如權(quán)限概覽頁面上顯示的提示。

  7. 檢查用戶的響應(yīng),他們可能會(huì)選擇同意或拒絕授予運(yùn)行時(shí)權(quán)限。

  8. 如果用戶向您的應(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

image-20210915223744044.png

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


image-20210915224030937.png

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


image-20210915224401907.png

image-20210915224517757.png

這里既是該方法的主要邏輯部分,其主要部分有:

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í):


image-20210915225605621.png

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


image-20210915225826560.png

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


image-20210915230209348.png

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


image-20210915230742936.png

2、Activity.requestPermissions

image-20210915232730639.png

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


image-20210915232840189.png

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


image-20210915233326976.png

這里簡要摘取些代碼片段介紹下大致流程

在GrantPermissionsActivity的onCreate方法中設(shè)置顯示布局:


image-20210915233528665.png

其中mViewHandler是GrantPermissionsViewHandlerImpl的對象


image-20210915233619193.png

查看其createView方法:


image-20210915233752223.png

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


image-20210915233918597.png

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


image-20210915234116407.png

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


image-20210915234210324.png

這里選擇允許時(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等


image-20210915234647841.png

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


image-20210915234927744.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容