Share via


外部記憶體

外部記憶體是指不在內部記憶體上的檔案記憶體,且無法獨佔存取負責檔案的應用程式。 外部記憶體的主要目的是提供一個位置,讓檔案在應用程式之間共用,或太大而無法放入內部記憶體。

從歷史上講,外部記憶體是指卸載式媒體上的磁碟分區,例如SD記憶卡(也稱為 可攜式儲存設備)。 此差異已不再與 Android 裝置演進相關,而且許多 Android 裝置不再支援卸載式儲存設備。 相反地,有些裝置會將部分內部非揮發性記憶體配置給Android,而Android可以執行與卸載式媒體相同的功能。 這稱為 模擬 記憶體,但仍被視為外部記憶體。 或者,某些 Android 裝置可能會有多個外部記憶體分割區。 例如,Android 平板電腦(除了其內部記憶體之外)可能已模擬 SD 記憶卡的一或多個插槽。 Android 會將所有這些分割區視為外部記憶體。

在擁有多個用戶的裝置上,每位用戶都會在其外部記憶體的主要外部記憶體磁碟分區上擁有專用目錄。 以一個使用者身分執行的應用程式將無法存取裝置上其他用戶的檔案。 所有使用者的檔案仍然是世界可讀取和可寫入的檔案;不過,Android 會將其他人的每位使用者配置檔沙盒化。

在 Xamarin.Android 中讀取和寫入檔案幾乎與任何其他 .NET 應用程式相同。 Xamarin.Android 應用程式會決定要操作的檔案路徑,然後使用標準 .NET 慣用語進行檔案存取。 由於內部和外部記憶體的實際路徑可能會因裝置到裝置或 Android 版本而異,因此不建議將檔案的路徑硬式編碼。 相反地,Xamarin.Android 會公開原生 Android API,以協助判斷內部和外部記憶體上的檔案路徑。

本指南將討論 Android 中外部記憶體特有的概念和 API。

外部記憶體上的公用和私人檔案

應用程式可能會保留在外部記憶體上的檔案類型有兩種:

  • 私人檔案 – 私人 檔案是您的應用程式專屬的檔案(但仍是世界可讀取和世界可寫入的檔案)。 Android 預期私人檔案會儲存在外部記憶體的特定目錄中。 即使檔案稱為「私人」,但裝置上的其他應用程式仍可看見和存取它們,但 Android 不會提供任何特殊保護。

  • 公用 檔案 – 這些檔案不是應用程式專屬的檔案,而且是要自由共用。

這些檔案之間的差異主要是概念性。 私人檔案是私用的,因為它們被視為應用程式的一部分,而公用檔案則是存在於外部記憶體上的任何其他檔案。 Android 提供兩個不同的 API 來解析私人和公用檔案的路徑,但否則相同的 .NET API 會用來讀取和寫入這些檔案。 這些是閱讀和寫入一節中所討論的相同 API。

私人外部檔案

私人外部檔案會被視為應用程式的特定檔案(類似於內部檔案),但因任何原因而保留在外部記憶體上(例如內部記憶體太大)。 與內部檔案類似,當使用者卸載應用程式時,將會刪除這些檔案。

呼叫 方法 Android.Content.Context.GetExternalFilesDir(string type)可找到私人外部檔案的主要位置。 這個方法會傳回 Java.IO.File 物件,代表應用程式的私人外部記憶體目錄。 傳遞 null 至此方法會傳回應用程式使用者記憶體目錄的路徑。 例如,對於封裝名稱 com.companyname.app為的應用程式,私人外部檔案的「root」 目錄會是:

/storage/emulated/0/Android/data/com.companyname.app/files/

本檔會將外部記憶體 上私人檔案的記憶體目錄稱為PRIVATE_EXTERNAL_STORAGE

GetExternalFilesDir()的參數是指定應用程式目錄字串。 這是一個目錄,旨在提供檔案邏輯組織的標準位置。 字串值可透過 類別上的 Android.OS.Environment 常數來取得:

Android.OS.Environment Directory
DirectoryAlarms PRIVATE_EXTERNAL_STORAGE/警示
DirectoryDcim PRIVATE_EXTERNAL_STORAGE/DCIM
DirectoryDownloads PRIVATE_EXTERNAL_STORAGE/下載
DirectoryDocuments PRIVATE_EXTERNAL_STORAGE/檔
DirectoryMovies PRIVATE_EXTERNAL_STORAGE/電影
DirectoryMusic PRIVATE_EXTERNAL_STORAGE/音樂
DirectoryNotifications PRIVATE_EXTERNAL_STORAGE/通知
DirectoryPodcasts PRIVATE_EXTERNAL_STORAGE/播客
DirectoryRingtones PRIVATE_EXTERNAL_STORAGE/鈴聲
DirectoryPictures PRIVATE_EXTERNAL_STORAGE/圖片

對於具有多個外部記憶體分割區的裝置,每個分割區都會有一個適用於私人檔案的目錄。 方法 Android.Content.Context.GetExternalFilesDirs(string type) 會傳回的 Java.IO.Files陣列。 每個物件都會代表所有共用/外部儲存裝置上的私人應用程式特定目錄,應用程式可以在其中放置其擁有的檔案。

重要

私人外部記憶體目錄的確切路徑可能會因裝置和 Android 版本而異。 因此,應用程式不得硬式編碼此目錄的路徑,而是改用 Xamarin.Android API,例如 Android.Content.Context.GetExternalFilesDir()

公用外部檔案

公用檔案是存在於未儲存在 Android 配置給私人檔案之目錄中之外部記憶體上的檔案。 卸載應用程式時,將不會刪除公用檔案。 Android 應用程式必須先獲得許可權,才能讀取或寫入任何公用檔案。 公用檔案可以存在於外部記憶體上的任何位置,但根據慣例,Android 預期公用檔案存在於 屬性 Android.OS.Environment.ExternalStorageDirectory所識別的目錄中。 這個屬性會傳回 Java.IO.File 代表主要外部記憶體目錄的物件。 例如, Android.OS.Environment.ExternalStorageDirectory 可能參考下列目錄:

/storage/emulated/0/

本檔會將外部記憶體 上公用檔案的記憶體目錄稱為PUBLIC_EXTERNAL_STORAGE

Android 也支援 PUBLIC_EXTERNAL_STORAGE 上的應用程式目錄概念。 這些目錄與 的應用程式 PRIVATE_EXTERNAL_STORAGE 目錄完全相同,如上一節的數據表所述。 方法 Android.OS.Environment.GetExternalStoragePublicDirectory(string directoryType) 會傳回 Java.IO.File 對應至公用應用程式目錄的物件。 參數 directoryType 是必要參數,不能是 null

例如,呼叫 Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDocuments).AbsolutePath 會傳回類似如下的字串:

/storage/emulated/0/Documents

重要

公用外部記憶體目錄的確切路徑可能會因裝置和 Android 版本而異。 因此,應用程式不得硬式編碼此目錄的路徑,而是改用 Xamarin.Android API,例如 Android.OS.Environment.ExternalStorageDirectory

使用外部記憶體

一旦 Xamarin.Android 應用程式取得檔案的完整路徑之後,它應該使用任何標準 .NET API 來建立、讀取、寫入或刪除檔案。 這會最大化應用程式的跨平臺相容程式代碼數量。 不過,嘗試存取 Xamarin.Android 應用程式之前,必須確定可以存取該檔案。

  1. 確認外部記憶體 – 視外部記憶體 的性質而定,應用程式可能無法掛接及使用。 所有應用程式都應該先檢查外部記憶體的狀態,再嘗試使用它。
  2. 執行運行時間許可權檢查 – Android 應用程式必須向使用者要求許可權,才能存取外部記憶體。 這表示應在任何檔案存取之前執行運行時間許可權要求。 Xamarin.Android 中的許可權指南包含 Android 許可權的詳細數據。

以下將討論這兩項工作。

確認外部記憶體可供使用

寫入外部記憶體之前的第一個步驟是檢查它是可讀取或可寫入的。 屬性 Android.OS.Environment.ExternalStorageState 會保存識別外部記憶體狀態的字串。 這個屬性會傳回代表狀態的字串。 下表是可能由 Environment.ExternalStorageState傳回的值清單ExternalStorageState

External 儲存體 State 描述
MediaBadRemoval 媒體突然被移除,而不會被適當地卸除。
MediaChecking 媒體存在,但正在進行磁碟檢查。
MediaEjecting 媒體正在卸除和退出。
MediaMounted 媒體已掛接,而且可以讀取或寫入。
MediaMountedReadOnly 媒體已掛接,但只能從中讀取。
MediaNofs 媒體存在,但不包含適用於 Android 的文件系統。
MediaRemoved 沒有媒體存在。
MediaShared 媒體存在,但未掛接。 它正透過USB與其他裝置共用。
MediaUnknown Android 無法辨識媒體的狀態。
MediaUnmountable 媒體存在,但無法由Android掛接。
MediaUnmounted 媒體存在,但未掛接。

大部分的Android應用程式只需要檢查是否已掛接外部記憶體。 下列代碼段示範如何確認已掛接外部記憶體以進行唯讀取或讀寫存取:

bool isReadonly = Environment.MediaMountedReadOnly.Equals(Environment.ExternalStorageState);
bool isWriteable = Environment.MediaMounted.Equals(Environment.ExternalStorageState);

外部記憶體許可權

Android 會將存取外部記憶體 視為危險的許可權,這通常需要使用者授與其存取資源的許可權。 用戶可以隨時撤銷此許可權。 這表示應在任何檔案存取之前執行運行時間許可權要求。 應用程式會自動獲得讀取和寫入其私人檔案的許可權。 應用程式可以在使用者授與許可權之後讀取和寫入屬於其他應用程式的私人檔案。

所有 Android 應用程式都必須在 AndroidManifest.xml宣告兩個外部記憶體的其中一個許可權。 若要識別許可權,下列兩 uses-permission 個元素之一必須新增至 AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

注意

如果使用者授與 WRITE_EXTERNAL_STORAGE,則 READ_EXTERNAL_STORAGE 也會隱含地授與 。 不需要在 AndroidManifest.xml要求這兩個許可權。

您也可以使用解決方案屬性[Android 指令清單] 索引標籤來新增權限:

Solution Explorer - Required Permissions for Visual Studio

一般而言,用戶必須核准所有危險的許可權。 外部記憶體的許可權是異常的,此規則有例外狀況,視應用程式執行的 Android 版本而定:

Flowchart of external storage permission checks

如需執行運行時間許可權要求的詳細資訊,請參閱 Xamarin.Android 中的許可權指南。 monodroid-sample LocalFiles 也示範執行運行時間許可權檢查的其中一種方式。

使用ADB授與和撤銷許可權

在開發 Android 應用程式的過程中,可能需要授與和撤銷許可權,以測試與運行時間許可權檢查相關的各種工作流程。 您可以使用 ADB 在命令提示字元中執行此動作。 下列命令行代碼段示範如何針對套件名稱 為 com.companyname.app 的 Android 應用程式,使用 ADB 授與或撤銷許可權:

$ adb shell pm grant com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

$ adb shell pm revoke com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

刪除檔案

任何標準 C# API 都可以用來從外部記憶體刪除檔案,例如 System.IO.File.Delete。 您也可以以犧牲程式代碼可移植性來使用JaVA API。 例如:

System.IO.File.Delete("/storage/emulated/0/Android/data/com.companyname.app/files/count.txt");