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 > 0
                && grantResults[0] == 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 = 0X12;

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章