Xamarin.Android 中的許可權

概觀

Android 應用程式會在自己的沙箱中執行,基於安全性考慮,無法存取裝置上的特定系統資源或硬體。 用戶必須先明確將許可權授與應用程式,才能使用這些資源。 例如,應用程式在未經使用者明確許可權的情況下,無法存取裝置上的 GPS。 如果應用程式在沒有權限的情況下嘗試存取受保護的資源,Android 將會擲回 Java.Lang.SecurityException

當開發應用程式時,應用程式開發人員會在AndroidManifest.xml中宣告許可權。 Android 有兩個不同的工作流程,可取得使用者對這些許可權的同意:

  • 針對以 Android 5.1 (API 層級 22) 或更低為目標的應用程式,安裝應用程式時發生許可權要求。 如果使用者未授與許可權,則不會安裝應用程式。 安裝應用程式之後,除了卸載應用程式之外,無法撤銷許可權。
  • 從 Android 6.0 開始(API 層級 23),用戶已獲得更多許可權控制權:只要應用程式安裝在裝置上,他們就可以授與或撤銷許可權。 此螢幕快照顯示Google連絡人應用程式的許可權設定。 它會列出各種許可權,並允許使用者啟用或停用許可權:

Sample Permissions screen

Android 應用程式必須在運行時間檢查,以查看其是否有權存取受保護的資源。 如果應用程式沒有許可權,則必須使用 Android SDK 提供的新 API 提出要求,使用者才能授與許可權。 權限分為兩個類別:

  • 一般許可權 – 這些許可權對使用者的安全性或隱私權造成很小的安全性風險。 Android 6.0 會在安裝時自動授與一般許可權。 如需一 般許可權的完整清單,請參閱 Android 檔。
  • 危險許可權 – 與一般許可權相反,危險許可權是保護使用者安全性或隱私權的許可權。 用戶必須明確授與這些專案。 傳送或接收SMS訊息是需要危險許可權的動作範例。

重要

許可權所屬的類別可能會隨著時間而變更。 未來 API 層級可能會將分類為「一般」許可權的許可權提升為危險許可權。

危險許可權會進一步細分為許可權群組 許可權群組會保留邏輯上相關的許可權。 當使用者將許可權授與許可權群組的一個成員時,Android 會自動將許可權授與該群組的所有成員。 例如, STORAGE 許可權群組同時保留 WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE 許可權。 如果使用者將許可權授與 , READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 許可權會自動同時授與。

在要求一或多個許可權之前,最佳做法是在要求許可權之前,先提供應用程式需要許可權的理由。 一旦用戶瞭解理由,應用程式就可以向使用者要求許可權。 藉由瞭解理由,用戶可以在想要授與許可權時做出明智的決定,並在不授與許可權時了解影響。

檢查和要求許可權的整個工作流程稱為 運行時間許可權 檢查,如下圖所示:

Run-time permission check flow chart

Android 支援連結庫會回傳一些新 API,以取得舊版 Android 的許可權。 這些反向移植的 API 會自動檢查裝置上的 Android 版本,因此不需要每次執行 API 層級檢查。

本檔將討論如何將許可權新增至 Xamarin.Android 應用程式,以及以 Android 6.0 (API 層級 23) 或更新版本為目標的應用程式應該如何執行運行時間許可權檢查。

注意

硬體的許可權可能會影響Google Play篩選應用程式的方式。 例如,如果應用程式需要相機的許可權,則 Google Play 將不會在未安裝相機的裝置上顯示 Google Play 商店中的應用程式。

需求

強烈建議 Xamarin.Android 專案包含 Xamarin.Android.Support.Compat NuGet 套件。 此套件會將許可權特定 API 回傳給舊版 Android,提供一個通用介面,而不需要持續檢查應用程式執行中的 Android 版本。

要求系統許可權

使用 Android 許可權的第一個步驟是在 Android 指令清單檔案中宣告許可權。 不論應用程式的目標 API 層級為何,都必須這麼做。

以 Android 6.0 或更新版本為目標的應用程式無法假設,因為使用者在過去某個時間點授與許可權,所以下次許可權將會有效。 以 Android 6.0 為目標的應用程式必須一律執行運行時間許可權檢查。 以 Android 5.1 或更低版本為目標的應用程式不需要執行執行時間許可權檢查。

注意

應用程式應該只要求所需的許可權。

在指令清單中宣告許可權

許可權會新增至具有 uses-permission 專案的AndroidManifest.xml。 例如,如果應用程式要找出裝置的位置,則需要微調和課程位置許可權。 下列兩個元素會新增至指令清單:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

您可以使用 Visual Studio 內建的工具支援來宣告權限:

  1. 按兩下 方案總管中的 [屬性],然後選取 屬性視窗 中的[Android 指令清單] 索引標籤:

    Required permissions in the Android Manifest tab

  2. 如果應用程式還沒有AndroidManifest.xml,請按兩下 [找不到AndroidManifest.xml]。按兩下即可新增一個 ,如下所示:

    No AndroidManifest.xml message

  3. [必要許可權] 清單中選取應用程式所需的任何許可權 ,然後儲存:

    Example CAMERA permissions selected

Xamarin.Android 會在建置階段自動新增一些許可權,以偵錯組建。 這可讓應用程式偵錯變得更容易。 特別是兩個值得注意的許可權是 INTERNETREAD_EXTERNAL_STORAGE。 這些自動設定的許可權不會出現在 [必要許可權 ] 列表中啟用。 不過,發行組建只會使用 [必要許可權] 列表中明確設定 的許可權

針對以 Android 5.1(API 層級 22) 或更低版本為目標的應用程式,不需要執行更多任務作。 將在 Android 6.0 (API 23 層級 23 層級 23) 或更新版本上執行的應用程式,應該繼續進行下一節,以瞭解如何執行運行時間許可權檢查。

Android 6.0 中的運行時間許可權檢查

ContextCompat.CheckSelfPermission方法(可搭配Android支援連結庫使用)用來檢查是否已授與特定許可權。 這個方法會傳回具有兩個 Android.Content.PM.Permission 值之一的列舉:

  • Permission.Granted – 已授與指定的許可權。
  • Permission.Denied – 尚未授與指定的許可權。

此代碼段是如何檢查活動中 相機 權限的範例:

if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int)Permission.Granted) 
{
    // We have permission, go ahead and use the camera.
} 
else 
{
    // Camera permission is not granted. If necessary display rationale & request.
}

最佳做法是通知用戶為什麼應用程式需要許可權,以便做出明智的決策來授與許可權。 其中一個範例是拍攝相片和地理標記的應用程式。 使用者很清楚相機許可權是必要的,但可能不清楚應用程式為何也需要裝置的位置。 理由應該會顯示訊息,以協助使用者了解為何需要位置許可權,以及需要相機許可權。

方法 ActivityCompat.ShouldShowRequestPermissionRationale 可用來判斷是否應該向用戶顯示理由。 如果應該顯示指定權限的理由,這個方法會傳回 true 。 這個螢幕快照顯示應用程式所顯示的零食列範例,說明應用程式為何必須知道裝置的位置:

Rationale for location

如果使用者授與許可權, ActivityCompat.RequestPermissions(Activity activity, string[] permissions, int requestCode) 應該呼叫 方法。 此方法需要下列參數:

  • activity – 這是要求許可權且由 Android 通知結果的活動。
  • permissions – 要求的許可權列表。
  • requestCode – 整數值,用來比對呼叫的許可權要求 RequestPermissions 結果。 這個值應大於零。

此代碼段是討論的兩種方法範例。 首先,會進行檢查,以判斷是否應該顯示許可權理由。 如果要顯示理由,則會以理由顯示零食店。 如果使用者在 Snackbar 中按兩下 [確定 ],則應用程式會要求許可權。 如果使用者不接受理由,則應用程式不應該繼續要求許可權。 如果未顯示理由,則活動會要求許可權:

if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.AccessFineLocation)) 
{
    // Provide an additional rationale to the user if the permission was not granted
    // and the user would benefit from additional context for the use of the permission.
    // For example if the user has previously denied the permission.
    Log.Info(TAG, "Displaying camera permission rationale to provide additional context.");

    var requiredPermissions = new String[] { Manifest.Permission.AccessFineLocation };
    Snackbar.Make(layout, 
                   Resource.String.permission_location_rationale,
                   Snackbar.LengthIndefinite)
            .SetAction(Resource.String.ok, 
                       new Action<View>(delegate(View obj) {
                           ActivityCompat.RequestPermissions(this, requiredPermissions, REQUEST_LOCATION);
                       }    
            )
    ).Show();
}
else 
{
    ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.Camera }, REQUEST_LOCATION);
}

RequestPermission 即使使用者已授與許可權,也可以呼叫 。 後續呼叫並非必要,但會提供使用者確認或撤銷許可權的機會。 呼叫 時 RequestPermission ,控制權會移交給操作系統,其會顯示UI以接受許可權:

Permssion Dialog

使用者完成之後,Android 會透過回呼方法將結果傳回至活動。 OnRequestPermissionResult 這個方法是介面的 ActivityCompat.IOnRequestPermissionsResultCallback 一部分,必須由 Activity 實作。 此介面具有單一方法, OnRequestPermissionsResultAndroid 會叫用此方法,以通知使用者選擇的活動。 如果使用者已授與許可權,則應用程式可以繼續使用受保護的資源。 如何實 OnRequestPermissionResult 作的范例如下所示:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    if (requestCode == REQUEST_LOCATION) 
    {
        // Received permission result for camera permission.
        Log.Info(TAG, "Received response for Location permission request.");

        // Check if the only required permission has been granted
        if ((grantResults.Length == 1) && (grantResults[0] == Permission.Granted)) {
            // Location permission has been granted, okay to retrieve the location of the device.
            Log.Info(TAG, "Location permission has now been granted.");
            Snackbar.Make(layout, Resource.String.permission_available_camera, Snackbar.LengthShort).Show();            
        } 
        else 
        {
            Log.Info(TAG, "Location permission was NOT granted.");
            Snackbar.Make(layout, Resource.String.permissions_not_granted, Snackbar.LengthShort).Show();
        }
    } 
    else 
    {
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

摘要

本指南討論了如何在Android裝置中新增和檢查許可權。 舊 Android 應用程式 (API 層級 23) 與新 Android 應用程式之間許可權運作方式的差異 (API 層級 <> 22)。 它討論了如何在 Android 6.0 中執行運行時間許可權檢查。