1. 程式人生 > >app在android 6.0或以上平臺版本執行過程中請求許可權

app在android 6.0或以上平臺版本執行過程中請求許可權

前言

從android 6.0(API 23)開始,安裝app時不需要對app的許可權申請進行授權,而是在app執行的時候,使用者才需要對app進行授權。這種流程精簡了app的安裝過程,使用者不需要在安裝或者升級app的時候進行授權操作。這同樣也給了使用者更多對app功能的控制能力;例如,使用者可以選擇給一個照相app訪問攝像頭的許可權,但不給它訪問裝置地理位置的許可權。使用者也可以通過app的設定介面,隨時撤銷對app授予的許可權。

系統許可權可以被分為兩大類,普通許可權和危險許可權:

  • 普通許可權不會引起使用者隱私洩露的風險。如果你的app在manifest檔案中列舉了一個普通許可權,系統會自動對申請的普通許可權直接授權。
  • 危險許可權允許app訪問使用者的機密資訊。如果的你app在它的manifest中列舉了一個普通許可權,系統會自動授權。但如果你列舉的是一個危險許可權,使用者就必須明確處理是否授權給你的app。

無論是哪個android版本,你的app都必須在它的manifest檔案上宣告它需要的所有普通許可權和危險許可權,詳情請參考宣告許可權。只不過,宣告許可權的結果因系統版本和app的target SDK不同而不同:

  • 如果裝置執行在android 5.1或者更低版本的系統上,或者app的target SDK是22或者更低:如果你的app在它的manifest檔案中聲明瞭一個危險許可權,那麼使用者在安裝app的時候就必須授權這個危險許可權給你的app;如果使用者不同意授予這個許可權,系統就拒絕安裝這個app。
  • 如果裝置執行在android 6.0或者更高的系統版本上,並且app的target SDK是23或者更高:app必須在manifest檔案中宣告所有需要的許可權,並且在app執行的過程中必須請求使用者對每個使用到的危險許可權進行授權。使用者可以選擇授權或者拒絕每一個許可權,如果使用者拒絕了授權,app同樣可以繼續執行,只是對應的功能無法使用。

注意: 從android 6.0(API 23)開始,不管app的target SDK是不是低版本的SDK,使用者都可以隨時取消任意app已經授權過的許可權。不管你的app是什麼target SDK版本,當你的app缺少一個需要的許可權時,你都應該測試的app功能是否正常。

本章節內容將探討如何使用android support library來檢查和請求許可權。android的framework在android 6.0(API 23)版本上提供了類似的方法。不過,相對來說使用support library的方法會比較簡單,因為這樣你的app在呼叫這些方法之前就不需要檢查當前執行的android系統版本是多少。

檢查許可權

如果你的app需要一個危險許可權,那麼你每次執行需要這個危險許可權的操作時,你都必須檢查是否已經申請到這個許可權。使用者隨時都可以撤銷授予過的許可權,因此,儘管一個app昨天可以正常使用攝像頭,但不代表今天仍然擁有使用攝像頭的許可權。

檢查你的app是否擁有過一個許可權,需要呼叫ContextCompat.checkSelfPermission()方法。例如,下面的程式碼片段檢查了activity是否擁有寫日曆的許可權:

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

如果app擁有這個許可權,方法會返回PackageManager.PERMISSION_GRANTED,app可以繼續執行相關操作。如果app不具備該許可權,方法會返回PERMISSION_DENIED,app需要明確請求使用者授予該許可權。

申請許可權

如果你的app需要使用一個在它的manifest檔案中宣告的危險許可權,那麼在使用之前它必須請求使用者授予該許可權。android提供了一系列方法供你申請許可權。請求這些方法會喚起一個標準的android對話方塊,這個對話方塊你不能自定義。

解釋app為什麼需要許可權

在一些情況下,你可能想要幫助使用者理解為什麼你的app需要一個許可權。例如,如果使用者啟動一個攝影app,使用者應該對app請求使用攝像頭的許可權不會感到驚訝,但是使用者可能無法理解為什麼app還要求訪問裝置的地理位置和通訊錄。在你申請一個許可權之前,你應該考慮給使用者提供一個解釋。但是要注意不要用解釋過多騷擾使用者;如果你提供了太多的解釋,使用者可能會厭煩你的app並且解除安裝掉。

只有在使用者已經拒絕了許可權請求的情況下,你才有可能需要向用戶提供一個解釋。如果使用者繼續嘗試使用需要申請許可權的功能,但是這個許可權之前已經被使用者拒絕,這就說明使用者可能不清楚為什麼app的這個功能需要申請許可權。類似這樣的情況,那麼顯示一個許可權申請解釋就是一種很好的解決方法。

為了幫助判斷使用者在什麼情況下需要一個許可權解釋,android提供了一個工具方法,ActivityCompat.shouldShowRequestPermissionRationale()。如果app之前申請過一個許可權,但這個許可權被使用者拒絕了,這個方法就會返回true。

注意: 如果使用者在過去拒絕許可權申請的同時還勾選了系統許可權申請對話方塊中的不再詢問選項,這個方法也會返回false。如果是在裝置許可權設定中禁止了app使用的許可權,用這個方法判斷這些許可權同樣也會返回false。

申請你需要的許可權

如果你的app目前不具備它所需要的許可權,app必須呼叫requestPermissions()多個過載方法中的一個來申請適當的許可權。這些方法中,你的app除了將所需要的許可權作為引數傳遞進去之外,還需要傳遞一個整型值作為本次許可權請求的標識碼。這些方法都是非同步的:方法會直接返回,等到使用者響應對話方塊之後,系統才會回撥app的回撥方法把結果傳遞回來,結果中包括app呼叫requestPermissions()方法時傳遞進來的請求碼。

下面的程式碼檢查app是否擁有讀取使用者聯絡人的許可權,如果沒有,在需要的時候會申請許可權:

// 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()方法之前進行這些操作,請參考上面小節解釋app為什麼需要許可權

處理許可權請求的響應

當你的app申請許可權之後,系統會顯示一個對話方塊給使用者。當用戶響應之後,系統會回撥你的app的onRequestPermissionsResulet()方法,將響應結果傳遞過來。你的app必須重寫這個方法來判斷申請的許可權是否被授予了。回撥方法會接收到與你傳遞到requestPermissions()方法中相同的請求碼。例如,如果一個app請求READ_CONTACTS訪問許可權,它可能需要以下的響應程式碼:

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

這個系統顯示的對話方塊會描述你的app需要訪問的許可權組,它不會列舉具體使用到的哪一個許可權。例如,如果你請求READ_CONTACTS許可權,系統對話方塊只會描述你的app需要訪問裝置的通訊錄。使用者只需要對每一個許可權組授權一次。一旦對一個許可權組授權之後,如果你的app請求同一個許可權組中的其它許可權(同樣也要在app的manifest檔案宣告),系統會自動授予這些許可權。當你請求這些同個許可權組中的其它許可權時,系統也會回撥你的onRequestPermissionsResult()方法並傳遞PERMISSION_GRANTED引數,回撥的結果就跟使用者已經通過系統對話方塊明確同意你的許可權申請一樣。

注意: 你的app仍然需要明確請求每一個它需要的許可權,不過使用者是否已經同意授權同個許可權組中的其它許可權。此外,每個許可權組包含的許可權在未來android的釋出版中也有可能會變化。你的程式碼邏輯不應該建立在使用到的幾個許可權是否被包含在同一個許可權組中的假設之上。

例如,假設你在app的manifest中聲明瞭READ_CONTACTS和WRITE_CONTACTS許可權。如果你已經請求過READ_CONTACTS許可權並且使用者通過授權申請,當你再請求WRITE_CONTACTS許可權時,系統會立即把這個許可權也授權給app,不需要再與使用者進行授權互動。

如果使用者拒絕了你的許可權申請,你的app應該採取適當的響應操作。例如,你的app可能需要顯示一個對話方塊來解釋為什麼你的app無法繼續執行下去的原因是需要被使用者拒絕的許可權。

當系統請求使用者授權時,使用者可以選擇告訴系統需要再次請求這個許可權。在這種情況下,無論app呼叫多少次requestPermissions()方法來申請許可權,系統都會直接決絕申請。系統會回撥你的onRequestPermissionsResult()回撥方法並且傳遞PERMISSION_DENIED引數值,整個回撥過程就跟使用者已經直接明確拒絕你的許可權申請一樣。這意味著當你呼叫requestPermissions()方法時,你不能假設與使用者直接互動的操作一定會發生。