天天看點

Android6.0運作時權限處理Android6.0運作時權限

Android6.0運作時權限

概述

今天在開發中遇到讀取app緩存檔案的時候,在6.0系統上直接空異常,檢視了manifest配置,其中也加入了相關的權限,于是想到Android 6.0的新特性。

Android 6.0在我們原有的AndroidManifest.xml聲明權限的基礎上,

又新增了運作時權限動态檢測,以下權限都需要在運作時判斷:

身體傳感器

- 月曆

- 攝像頭

- 通訊錄

- 地理位置

- 麥克風

- 電話

- 短信

- 存儲空間

對于6.0的幾個主要的變化,檢視檢視官網的這篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中也包含Runtime Permissions

官網權限相關文章:

Working with System Permissions

Permissions Best Practices

https://developer.android.com/preview/features/runtime-permissions.html

https://developer.android.com/preview/features/runtime-permissions.html#support-lib

https://developer.android.com/preview/features/runtime-permissions.html#normal

https://developer.android.com/reference/android/content/pm/PermissionInfo.html#PROTECTION_NORMAL

問題及特點

  • 運作在android M時才運作時權限

    新的運作時權限僅當我們設定targetSdkVersion to 23(這意味着你已經在23上測試通過了)才起作用,當然還要是M系統的手機。app在6.0之前的裝置依然使用舊的權限系統。

  • 分組對我們的權限機制有什麼影響嗎?

    通過adb shell pm list permissions -d -g進行權限的檢視。

    看到上面的dangerous permissions,會發現一個問題,好像危險權限都是一組一組的

    如果app運作在Android 6.x的機器上,對于授權機制是這樣的。如果你申請某個危險的權限,假設你的app早已被使用者授權了同一組的某個危險權限,那麼系統會立即授權,而不需要使用者去點選授權。比如你的app對READ_CONTACTS已經授權了,當你的app申請WRITE_CONTACTS時,系統會直接授權通過。此外,對于申請時彈出的dialog上面的文本說明也是對整個權限組的說明,而不是單個權限(ps:這個dialog是不能進行定制的)。

    不過需要注意的是,不要對權限組過多的依賴,盡可能對每個危險權限都進行正常流程的申請,因為在後期的版本中這個權限組可能會産生變化。

  • targetSdkVersion 低于 23在android 6.0上運作會有這個權限問題嗎?

    如果app的targetSdkVersion 低于 23,那将被認為app沒有用23新權限測試過,那将被繼續使用舊有規則:使用者在安裝的時候不得不接受所有權限,安裝後app就有了那些權限咯!然後app像以前一樣奔跑!注意,此時使用者依然可以取消已經同意的授權!使用者取消授權時,android 6.0系統會警告,但這不妨礙使用者取消授權。

    問題又來了,這時候你的app崩潰嗎?

    善意的主把這事也告訴了android小組,當我們在targetSdkVersion 低于23的app調用一個需要權限的函數時,這個權限如果被使用者取消授權了的話,不抛出異常。但是他将啥都不幹,結果導緻函數傳回值是null或者0.

  • 釋出時注意

    代碼沒有成功改為支援最新運作時權限的app,不要設定targetSdkVersion 23 釋出,否則你就有麻煩了。隻有當你測試過了,再改為targetSdkVersion 23 。

    警告:現在你在android studio建立項目,targetSdkVersion 會自動設定為 23。如果你還沒支援新運作時權限,我建議你首先把targetSdkVersion 降級到22

運作時權限擷取

1.在AndroidManifest檔案中添加需要的權限。

這個步驟和我們之前的開發并沒有什麼變化,試圖去申請一個沒有聲明的權限可能會導緻程式崩潰
           

2.檢查權限

if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
    }else{
        //
    }
           
ContextCompat.checkSelfPermission,主要用于檢測某個權限是否已經被授予,方法傳回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當傳回DENIED就需要進行申請授權了
           

3.申請授權

ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);
           
該方法是異步的,第一個參數是Context;第二個參數是需要申請的權限的字元串數組;第三個參數為requestCode,主要用于回調的時候檢測。可以從方法名requestPermissions以及第二個參數看出,是支援一次性申請多個權限的,系統會通過對話框逐一詢問使用者是否授權。
           

4.處理權限申請回調

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 
                && grantResults[] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
    }
}
           
對于權限的申請結果,首先驗證requestCode定位到你的申請,然後驗證grantResults對應于申請的結果,這裡的數組對應于申請時的第二個權限字元串數組。如果你同時申請兩個權限,那麼grantResults的length就為2,分别記錄你兩個權限的申請結果。
           

5.申請權限的解釋

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
        Manifest.permission.READ_CONTACTS)) 
    // Show an expanation to the user *asynchronously* -- don't block
    // this thread waiting for the user's response! After the user
    // sees the explanation, try again to request the permission.

}
           
這個API主要用于給使用者一個申請權限的解釋,該方法隻有在使用者在上一次已經拒絕過你的這個權限申請。也就是說,使用者已經拒絕一次了,你又彈個授權框,你需要給使用者一個解釋,為什麼要授權,則使用該方法。
           

到此,常用的方法是:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}
           

處理 “不再提醒”

如果使用者拒絕某授權。下一次彈框,使用者會有一個“不再提醒”的選項的來防止app以後繼續請求授權。如果這個選項在拒絕授權前被使用者勾選了。下次為這個權限請求requestPermissions時,對話框就不彈出來了,結果就是,app啥都不幹。這将是很差的使用者體驗,使用者做了操作卻得不到響應。這種情況需要好好處理一下。在請求requestPermissions前,我們需要檢查是否需要展示請求權限的提示通過activity的shouldShowRequestPermissionRationale,代碼如下:

final private int REQUEST_CODE_ASK_PERMISSIONS = ;

private void insertDummyContactWrapper() {
    int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
    if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
//TODO
            if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
                showMessageOKCancel("You need to allow access to Contacts",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                                        REQUEST_CODE_ASK_PERMISSIONS);
                            }
                        });
                return;
            }
        requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                REQUEST_CODE_ASK_PERMISSIONS);
        return;
        //TODO
    }
    ...;
}

private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setPositiveButton("OK", okListener)
            .setNegativeButton("Cancel", null)
            .create()
            .show();
}
           

後記

權限處理并不複雜,但是需要編寫很多重複的代碼,在github上面的幾個庫:

https://github.com/hotchemi/PermissionsDispatcher

https://github.com/lovedise/PermissionGen

https://github.com/hongyangAndroid/MPermissions.(推薦)

實作明天理想的唯一障礙是今天的疑慮, tks