Android 6.0 (API 23, M)以上對permission的變化

背景

剛開始工做,最近在看Android經典入門書籍《第一行代碼》。後來才發現看的是初版,因爲Android版本升級,各類feature變了不少,因此書裏有些例子已經不能在新一點的版本上正確運行了,我就踩到了一個坑,是關於Android 6.0之後系統對於應用permission的處理的變化。html

踩坑過程

在《第一行代碼》「8.2.1 接收短信」這一節裏,書中給了一個demo項目,主要功能是一旦手機接收到短信的時候,app自動顯示出發信人和短信內容。原理很簡單:系統在收到短信的時候,會發出值爲「android.provider.Telephony.SMS_RECEIVED」的一條廣播,因此只需在代碼裏註冊一個BroadcaseReceiver接收這個廣播而且讀出短信信息而後顯示到屏幕上就好了。android

高高興興的照着書敲完代碼,在模擬器上運行程序,打開DDMS編輯模擬短息,send~走你~duang~~短信接收到了,可是應用上什麼都沒顯示,立馬傻眼。檢查了一遍代碼,確認沒錯,又嘗試一次,duang~仍是不行。因而開始懷疑是否是DDMS有什麼問題,就用本身的手機(Android 5.0)試了一下,結果程序正常了,應用成功的顯示了短信發信人和內容。這證實模擬器上確實是有什麼地方不對勁,看了一眼Virtual Device Manager,我用的模擬器是Android 7.0,聯想到以前Android 7.0上下載和打開文件所遇到的坑,警戒的懷疑是否是因爲版本變化所引發的不一樣。因而在Android 5.0的模擬器上又嘗試的一下,duang~成功了。這就證實了確實是版本升級所引發的不一樣。app

填坑過程

肯定了問題緣由,開始搜索資料,通過一番調查,其實Android官方文檔對permission的行爲寫的很清楚,在這裏總結記錄一下。異步

Android把permission分爲兩種:async

  • normal permission:不會直接威脅用戶的隱私ide

  • danger permission:可能會訪問用戶的隱私數據測試

應用要在Manifest文件中聲明本身所須要的permission,然而用戶在安裝應用的時候,系統版本不一樣,則會有不一樣的對permission的處理:this

  • 對於Android 5.1 (API 22)或更低版本的系統(如下簡稱「低版本系統」),在用戶安裝應用的時候,系統會展現應用所需的全部permission的列表(包括normal permission和danger permission),並詢問用戶是否贊成授予全部權限。若是用戶由於不想授予某一或某些權限,應用則不會被安裝。code

  • 對於Android 6.0 (API 23)或者更高版本(如下簡稱「高版本系統」),在用戶安裝應用的時候,系統會自動授予應用所需的全部normal permission並安裝程序,可是並不授予應用所需的danger permission。用戶有機會在應用運行時選擇是否授予全部或部分danger permission,而且能夠隨時在設置裏收回授予給應用的任何danger permission。orm

看到這裏區別就比較明瞭了,對於低版本系統,若是不想授予某些權限,則應用根本不會被安裝。對於高版本系統,若是你不想授予某些danger permission,只要選擇不授予就能夠了,不會影響應用安裝,也不影響應用運行,可是某些feature可能就不能用了(好比你不想授予讀取短信功能,那麼應用裏與短信相關的feature就不能用了,可是與短信無關的feature可使用)。這樣有一個好處就是用戶能夠控制不授予應用哪些數據,而即便不授予應用某些數據,用戶仍然可使用「殘血版」的應用。

請求permission

高版本系統在運行時提供給用戶授予permission的機會,因此開發者要在代碼中對檢查和請求permission作相應的處理,在Android framework裏提供了相應的方法,在Support Library裏也提供了相似的方法。Android的官方文檔推薦使用Support Library裏的方法,由於使用更簡單。

檢查permission

可使用ContextCompat.checkSelfPermission()來檢查是否已經得到了某個permission:

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

若是應用已經獲取了permission,則返回PackageManager.PERMISSION_GRANTED,若是沒有獲取,則返回PERMISSION_DENIED。

請求permission

若是應用沒有得到某個permission,則能夠經過調用requestPermissions()來請求權限

// 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 explanation 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.
    }
}

上述代碼中在調用requestPermissions()以前先調用了一個方法shouldShowRequestPermissionRationale(),這個方法給應用一個機會向用戶解釋爲何要請求這個permission。

當requestPermissions()這個方法被調用時,系統會彈出一個樣式固定的彈窗,這個彈窗不能定製,供用戶選擇是否授予permission。requestPermissions()是異步執行的,這個方法會立刻返回。在用戶作出選擇以後,系統會調用onRequestPermissionsResult(),對請求permission的結果是在這個方法中處理的

@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;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

看到這,其實個人問題已經有了解決方法,先檢查是否有讀取短信權限,若是有的話直接註冊廣播接收器,若是沒有的話請求權限,若是得到權限以後再註冊廣播接收器,運行,duang~成功~

更多知識

permission group

Android的permission有個權限組(permission group的概念),好比RECEIVE_SMS、READ_SMS、SEND_SMS......都屬於SMS組,在應用已經得到了某一組中的某一權限,則下次再請求同組中的其餘權限時,系統不會詢問用戶則直接授予請求的權限。

充分測試

官方文檔提醒開發者,因爲用戶可能不授予請求的permission,全部開發者要充分測試各類狀況,確保在有沒有權限的時候應用要有合理的表現。

官方連接

對於記錄的不是很詳細的地方,能夠參考官方連接:https://developer.android.com...

相關文章
相關標籤/搜索