Android開發——Android 6.0許可權管理機制詳解

來源:互聯網
上載者:User

標籤:

0.前言

最近在研究所實習,我負責維護Android手機取證項目的Android用戶端,有客戶反映我們的APP在Android6.0無響應,經過調試發現SD卡讀寫權限許可權被拒絕。但明明是在AndroidManifest.xml檔案中聲明過的。查了很多資料才知道Android6.0的很多許可權申請機制發生了改變,可以說是Android6.0在安全機制上更進了一步吧,因此寫下這篇文章以記錄。

註:在運行程式時,對於某些許可權向使用者詢問申請(後面會詳細地講)時因為我們知道客戶在我們APP中不會點“拒絕”,因此我對此功能的實現也僅限於文章中的前部分直接做死迴圈直到使用者同意授權該許可權,因為當然是用最低的成本滿足客戶的需求最好啦,但是真正開發中,需要處理很多事情,我也並沒有淺嘗輒止,後面會用到onRequestPermissionsResult回調方法,shouldShowRequestPermissionRationale方法等,才能避免很多令使用者困擾的情況。後面會詳細地進行介紹。

我也花了整整兩天的時間對相容Android6.0許可權管理機制的整個處理過程進行了理解和匯總,也方便大家遇到類似的問題少走彎路。


1.Android 6.0新的許可權管理機制

Android 6.0 Marshmallow版本之後,系統對於一些危險層級的許可權,在運行那些targetSdkVersion設定為23和23以上的應用並且需要這些許可權時,會一個一個詢問使用者是否授予許可權。若不詢問直接使用這些許可權,會出現類似java.lang.SecurityException: Permission Denial的異常日誌。

應用targetSdkVersion如果沒有設定為23版本或者以上,系統還是會使用舊規則:在安裝的時候賦予該app所申請的所有許可權。因此不會影響以前應用的正常使用,但是6.0以後,使用者可以在<設定-許可權>裡將該APP的某些許可權手動關閉,此時被使用者禁止許可權的API介面傳回值都為null或者0,我們判空即可防止App Crash。

若APP在運行時,將設定裡的許可權手動關閉,那就會直接Crash。

 

2.危險許可權列表

前面提到的危險層級的許可權是我們需要格外關注的,因為這些許可權在使用前需要進行特殊處理。

 

中有一個許可權群的概念,同一組的任何一個許可權被授權了,其他許可權也自動被授權。

例如,一旦WRITE_EXTERNAL_STORAGE被授權了,APP同時也就有了READ_EXTERNAL_STORAGE許可權。

 

3.Android 6.0運行時主動請求許可權


3.1  檢測和申請許可權

下面的例子介紹上面列出的讀寫SD卡的使用例子,可以使用以下的方式解決:

public boolean isGrantExternalRW(Activity activity) {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(            Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {        activity.requestPermissions(new String[]{                Manifest.permission.READ_EXTERNAL_STORAGE,                Manifest.permission.WRITE_EXTERNAL_STORAGE        }, 1);        return false;//第一次開啟應用並執行許可權檢查,雖然返回了false,但是已經調用過了申請許可權的方法    }    return true;//非第一次開啟應用並執行許可權檢查,或者6.0以下的Android版本}

這裡需要說明的是,檢查和申請許可權的方法分別是Activity.checkSelfPermission()和Activity.requestPermissions,這兩個方法是在 API 23中新增的。

Activity.checkSelfPermission()主要用於檢測某個許可權是否已經被授予,方法傳回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當返回DENIED就需要進行申請授權了。

Activity.requestPermissions該方法是非同步,第一個參數是需要申請的許可權的字串數組,第二個參數為requestCode,主要用於回調的時候檢測。最前面的參數可以傳入mActivity。好像不傳也可以。

可以從方法名requestPermissions以及第二個參數看出,是支援一次性申請多個許可權的,系統會通過對話方塊逐一詢問使用者是否授權。

然後在需要使用這個許可權的時機,進行如下調用即可。

 boolean isGrant= isGrantExternalRW(mActivity); if (isGrant) {      //商務邏輯 } while (!isGrant) {      try {         Thread.sleep(1000);} catch (InterruptedException e) {        e.printStackTrace();}    isGrant = isGrantExternalRW(mAct);    if (isGrant) {       //商務邏輯   }}

這裡因為我們寫的isGrantExternalRW方法的傳回值只是判斷的一開始檢查許可權時的狀態,因此如果是第一次開啟應用,返回的是false,並且申請了許可權(如果使用者同意的話),再調用一次該方法返回true,進入邏輯代碼,並最後跳出while迴圈。如果使用者已經授權過了,那麼直接會走邏輯代碼。這裡比較流氓的是,如果使用者一直不同意,會一直返回false,我們就阻塞在while迴圈裡,一秒後繼續申請,直到使用者同意為止。顯然這是不夠友好的。

因此Google為了防止這種情況的發生,在使用者拒絕授權時,下一次彈窗可以勾選“不再提醒”。如果這個選項被使用者勾選了。下次為這個許可權請求requestPermissions時,對話方塊就不彈出來了,系統會直接回調處理申請返回結果的回調方法onRequestPermissionsResult,回調結果為最後一次使用者的選擇。


3.2 處理許可權申請回調

@Overridepublic void onRequestPermissionsResult(int requestCode,        String permissions[], int[] grantResults) {    switch (requestCode) {        case 1: {            // 使用者取消授權這個數組為空白,如果你同時申請兩個許可權,那麼grantResults的length就為2,分別記錄你兩個許可權的申請結果            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                //商務邏輯            } else {                //授權被拒絕,不再進行基於該許可權的功能            }            return;        }        // 其他case處理其他許可權的申請回調    }}

上面處理申請回調的方法已經寫的很明白了。還有就是不管使用者點擊拒絕還是同意,都會回調該方法(別忘記使用者拒絕並勾選“不再提醒”時也會回調)。


3.3 使用shouldShowRequestPermissionRationale方法


問題來了,如果第二次向使用者申請許可權被拒絕,並且使用者勾選了“不再提醒”,那我們以後每次需要使用這個許可權都會直接被拒絕,並不會彈出對話方塊。APP什麼也不做會產生很差的使用者體驗。所以這種情況需要我們進行處理。這時候我們可以藉助shouldShowRequestPermissionRationale方法。首先看一下該方法的傳回值。


因此,我們在回呼函數中做許可權檢測,如果返回DENIED,就調用上述方法,返回false,就彈出對話方塊引導使用者手動開啟許可權,避免了使用者的操作觸發了許可權申請機制(已被拒絕並勾選不再提醒或手動關閉許可權),但是沒有任何響應的尷尬。又因為我們事先向使用者詢問授權過了,因此不存在表格中的第一種情況。

因此只需要在上面回調代碼的else程式碼片段加入如下代碼即可。

                if (!mActivity.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {                    //使用者已經完全拒絕,或手動關閉了許可權                    //開啟此對話方塊緩解一下尷尬...                    AlertDialog dialog = new AlertDialog.Builder(this)                            .setMessage("不開啟該許可權將無法正常工作,請在設定中手動開啟!")                            .setPositiveButton("確定", new DialogInterface.OnClickListener() {                                @Override                                public void onClick(DialogInterface dialog, int which) {                                    finish();                                }                            })                            .setNegativeButton("取消", new DialogInterface.OnClickListener() {                                @Override                                public void onClick(DialogInterface dialog, int which) {                                    finish();                                }                            }).create();                    dialog.show();                    return;                }else{                    //使用者一直拒絕並一直不勾選“不再提醒”                    //Toast提醒一下即可                }

在需要許可權的時候,只要執行類似於下面的檢查和申請許可權的代碼即可(注意自己替換ContentCompat):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {     ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); } else {     //Do the stuff that requires permission... }




Android開發——Android 6.0許可權管理機制詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.