Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android permission權限與安全機制解析(下)

android permission權限與安全機制解析(下)

編輯:關於android開發

android permission權限與安全機制解析(下)


 

android 6.0權限全面詳細分析和解決方案

Marshmallow版本權限修改

  android的權限系統一直是首要的安全概念,因為這些權限只在安裝的時候被詢問一次。一旦安裝了,app可以在用戶毫不知曉的情況下訪問權限內的所有東西,而且一般用戶安裝的時候很少會去仔細看權限列表,更不會去深入了解這些權限可能帶來的相關危害。所以在android 6.0 Marshmallow版本之後,系統不會在軟件安裝的時候就賦予該app所有其申請的權限,對於一些危險級別的權限,app需要在運行時一個一個詢問用戶授予權限。
  這裡寫圖片描述

舊版本app兼容問題

  那麼問題來了,是不是所有以前發布的app都會出現問題呢?答案是不會,只有那些targetSdkVersion 設置為23和23以上的應用才會出現異常,在使用危險權限的時候系統必須要獲得用戶的同意才能使用,要不然應用就會崩潰,出現類似

java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider

的崩潰日志。所以targetSdkVersion如果沒有設置為23版本或者以上,系統還是會使用舊規則:在安裝的時候賦予該app所申請的所有權限。所以app當然可以和以前一樣正常使用了,但是還有一點需要注意的是6.0的系統裡面,用戶可以手動將該app的權限關閉,如下圖
  這裡寫圖片描述
  那麼問題又來了,如果以前的老應用申請的權限被用戶手動關閉了怎麼辦,應用會崩潰麼?我們來試一試
  這裡寫圖片描述
  好吧,可以慶幸了一下了,不會拋出異常,不會崩潰,只不過調用那些被用戶禁止權限的api接口返回值都為null或者0,所以我們只需要做一下判空操作就可以了,不判空當然還是會崩潰的喽。

普通權限和危險權限列表

  現在對於新版本的權限變更應該有了基本的認識,那麼,是不是所有權限都需要去進行特殊處理呢?當然不是,只有那些危險級別的權限才需要,如列表所示:
  這裡寫圖片描述
  android開發者官網也有相關描述:
  http://developer.android.com/training/permissions/requesting.html
  http://developer.android.com/guide/topics/security/permissions.html
  所以仔細去看看自己的app,對照列表,如果有需要申請其中的一個權限,就需要進行特殊操作。還有一個比較人性的地方就是如果同一組的任何一個權限被授權了,其他權限也自動被授權。例如,一旦WRITE_EXTERNAL_STORAGE被授權了,app也有READ_EXTERNAL_STORAGE權限了。

支持Marshmallow新版本權限機制

  終於要開始支持android 6.0版本了,最先一步當然就是修改build.gradle文件中的tragetSdkVersion和compileSdkVersion成23版本,同時使用compile ‘com.android.support:appcompat-v7:23.1.1’最新v7包。

android {
    compileSdkVersion 23
    ...

    defaultConfig {
        ...
        targetSdkVersion 23
        ...
    }
}
...
dependencies {
...
compile 'com.android.support:appcompat-v7:23.1.1'
...

  修改完後,感興趣的朋友可以直接打包在手機上測試一下,看看是不是會出現類似於上面我說的那些崩潰日志。
  接著下一步當然就是要修改代碼了,最原始代碼,無任何處理:

private void startGetImageThread(){
....
    Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    ContentResolver contentResolver = getContentResolver();
    //獲取jpeg和png格式的文件,並且按照時間進行倒序
    Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=\"image/jpeg\" or " +
    MediaStore.Images.Media.MIME_TYPE + "=\"image/png\"", null, MediaStore.Images.Media.DATE_MODIFIED+" desc");
    ....
}

  這段代碼需要訪問外部存儲(相冊圖片),屬於危險級別的權限,直接使用會造成應用崩潰,所以在這段代碼執行之前我們需要進行特殊處理:

int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
                CODE_FOR_WRITE_PERMISSION);
    return;
}

  寫完這段代碼之後,就會出現如下系統dialog:
  這裡寫圖片描述
  緊接著就需要去處理DENY和ALLOW的回調了,重寫onRequestPermissionsResult函數:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == CODE_FOR_WRITE_PERMISSION){
        if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
            //用戶同意使用write
            startGetImageThread();
        }else{
            //用戶不同意,自行處理即可
            finish();
        }
    }
}

  好了,這樣就算是簡單初步適配完成了。

處理不再提醒

  如果用戶拒絕某授權。下一次彈框,用戶會有一個“不再提醒”的選項的來防止app以後繼續請求授權。
  這裡寫圖片描述
  如果這個選項在拒絕授權前被用戶勾選了。下次為這個權限請求requestPermissions時,對話框就不彈出來了,系統會直接回調onRequestPermissionsResult函數,回調結果為最後一次用戶的選擇。所以為了應對這種情況,系統提供了一個shouldShowRequestPermissionRationale()函數,這個函數的作用是幫助開發者找到需要向用戶額外解釋權限的情況,這個函數:

應用安裝後第一次訪問,直接返回false;第一次請求權限時,用戶拒絕了,下一次shouldShowRequestPermissionRationale()返回 true,這時候可以顯示一些為什麼需要這個權限的說明;第二次請求權限時,用戶拒絕了,並選擇了“不再提醒”的選項時:shouldShowRequestPermissionRationale()返回 false;設備的系統設置中禁止當前應用獲取這個權限的授權,shouldShowRequestPermissionRationale()返回false;  注意:第二次請求權限時,才會有“不再提醒”的選項,如果用戶一直拒絕,並沒有選擇“不再提醒”的選項,下次請求權限時,會繼續有“不再提醒”的選項,並且shouldShowRequestPermissionRationale()也會一直返回true。
  所以利用這個函數我們可以進行相應的優化,針對shouldShowRequestPermissionRationale函數返回false的處理有兩種方法:如果應用是第一次請求該權限,則直接調用requestPermissions函數去請求權限;如果不是則代表用戶勾選了’不再提醒’,彈出dialog,告訴用戶為什麼你需要該權限,讓用戶自己手動開啟該權限。鏈接:http://stackoverflow.com/questions/32347532/android-m-permissions-confused-on-the-usage-of-shouldshowrequestpermissionrati在onRequestPermissionsResult函數中進行檢測,如果返回PERMISSION_DENIED,則去調用shouldShowRequestPermissionRationale函數,如果返回false代表用戶已經禁止該權限(上面的3和4兩種情況),彈出dialog告訴用戶你需要該權限的理由,讓用戶手動打開。鏈接:http://stackoverflow.com/questions/30719047/android-m-check-runtime-permission-how-to-determine-if-the-user-checked-nev  處理方法已經有了,修改一下代碼,我這裡就以第二種方案來處理了:

 

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == CODE_FOR_WRITE_PERMISSION){
        if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
            //用戶同意使用write
            startGetImageThread();
        }else{
            //用戶不同意,向用戶展示該權限作用
            if (!ActivityCompat.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;
            }
            finish();
        }
    }
}

  當勾選不再提醒,並且拒絕之後,彈出dialog,提醒用戶該權限的重要性:
  這裡寫圖片描述
  搞定!!!

使用兼容庫

  以上的代碼在6.0版本上使用沒有問題,但是在之前就有問題了,最簡單粗暴的解決方法可能就是利用Build.VERSION.SDK_INT >= 23這個判斷語句來判斷了,方便的是SDK 23的v4包加入了專門類進行相關的處理:

ContextCompat.checkSelfPermission()被授權函數返回PERMISSION_GRANTED,否則返回PERMISSION_DENIED ,在所有版本都是如此。ActivityCompat.requestPermissions()這個方法在6.0之前版本調用,OnRequestPermissionsResultCallback 直接被調用,帶著正確的 PERMISSION_GRANTED或者PERMISSION_DENIED。ActivityCompat.shouldShowRequestPermissionRationale()在6.0之前版本調用,永遠返回false。  用v4包的這三方法,完美兼容所有版本!下面是代碼:
//使用兼容庫就無需判斷系統版本
int hasWriteContactsPermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteContactsPermission == PackageManager.PERMISSION_GRANTED) {
    startGetImageThread();
}
//需要彈出dialog讓用戶手動賦予權限
else{
    ActivityCompat.requestPermissions(PickOrTakeImageActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE_FOR_WRITE_PERMISSION);
}

  onRequestPermissionsResult函數不變。後兩個方法,我們也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale()和activity效果一樣。

一次請求多個權限

  當然了有時候需要多個權限,可以用上面方法一次請求多個權限。當然最重要的是不要忘了為每個權限檢查“不再提醒”的設置。

List permissionsNeeded = new ArrayList();
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
permissionsNeeded.add(Manifest.permission.READ_CONTACTS);
permissionsNeeded.add(Manifest.permission.WRITE_CONTACTS);
requestPermissions(permissionsNeeded.toArray(new String[permissionsList.size()]), CODE_FOR_MULTIPLE_PERMISSION);

  最後在onRequestPermissionsResult函數中一個個處理返回結果即可。

第三方庫簡化代碼

  當然早就有第三方庫來幫忙做這些事情了:
  Github上的開源項目 PermissionHelper和hotchemi’s PermissionsDispatcher

APP處於運行狀態下,被撤銷權限

  如果APP正在運行中,用戶進入設置-應用程序頁面去手動撤銷該APP權限,會出現什麼情況呢?哈哈,系統又會接著彈出權限請求對話框,挺好挺好:
  這裡寫圖片描述
  這樣就沒有問題了吧O(∩_∩)O~
  上面的測試環境為genymotion6.0模擬器,有朋友跟我反映在6.0nexus 6p真機上會直接退出應用,所以這個應該還和測試環境有關。

結論建議

  新運行時權限已經在棉花糖中被使用了。我們沒有退路。我們現在唯一能做的就是保證app適配新權限模型。欣慰的是只有少數權限需要運行時權限模型。大多數常用的權限,例如,網絡訪問,屬於Normal Permission 在安裝時自動會授權,當然你要聲明,以後無需檢查。因此,只有少部分代碼你需要修改。
兩個建議:
  1.嚴肅對待新權限模型。
  2.如果你代碼沒支持新權限,不要設置targetSdkVersion 23 。尤其是當你在Studio新建工程時,不要忘了修改!
  說一下代碼修改。這是大事,如果代碼結構被設計的不夠好,你需要一些很蛋疼的重構。每個app都要被修正。如上所說,我們沒的選擇。列出所有你需要請求權限的全部情形,如果A被授權,B被拒絕,會發生什麼,針對每一個情況認真處理。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved