使用 Xamarin.Android 檔案 儲存體 和 Access

Android 應用程式的常見需求是操作檔案 – 儲存圖片、下載檔或匯出數據以與其他程序共用。 Android (以 Linux 為基礎)藉由提供檔案儲存空間來支援此功能。 Android 會將檔案系統分成兩種不同類型的記憶體:

  • 內部 儲存體 – 這是檔案系統的一部分,只能由應用程式或作業系統存取。
  • 外部 儲存體 – 這是可供所有應用程式、使用者和其他裝置存取之檔案記憶體的分割區。 在某些裝置上,外部存放裝置可能是可移動的(例如 SD 記憶卡)。

這些群組只是概念性,而且不一定參考裝置上的單一分割區或目錄。 Android 裝置一律會提供內部記憶體和外部記憶體的數據分割。 某些裝置可能會有多個分割區被視為外部記憶體。 不論 API 用於讀取、寫入或建立檔案的分割區都相同。 有兩組 API 可供 Xamarin.Android 應用程式用於檔案存取:

  1. .NET API(由 Mono 提供並由 Xamarin.Android 包裝) – 其中包括 Xamarin.Essentials 提供的文件系統協助程式。 .NET API 提供最佳的跨平臺相容性,因此本指南的重點將放在這些 API 上。
  2. 原生 Java 檔案存取 API(由 Java 提供且由 Xamarin.Android 包裝) – Java 提供自己的 API 來讀取和寫入檔案。 這些是完全可接受的 .NET API 替代方案,但專屬於Android,不適用於想要跨平台的應用程式。

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

在討論與檔案存取相關的 API 之前,請務必了解內部和外部記憶體的相關一些詳細數據。 下一節將討論這一點。

內部與外部記憶體

從概念上講,內部記憶體和外部記憶體非常類似 – 兩者都是 Xamarin.Android 應用程式可能會儲存盤案的位置。 對於不熟悉 Android 的開發人員來說,這種相似性可能會造成混淆,因為目前還不清楚應用程式何時應該使用內部記憶體與外部記憶體。

內部記憶體是指 Android 配置給操作系統、APK 和個別應用程式的非揮發性記憶體。 除了作業系統或應用程式之外,無法存取此空間。 Android 會為每個應用程式配置內部記憶體分割區中的目錄。 卸載應用程式時,也會刪除該目錄中內部記憶體上保留的所有檔案。 內部記憶體最適合只有應用程式可存取的檔案,且不會與其他應用程式共用,或一旦卸載應用程式,其價值將很少。 在 Android 6.0 或更高版本上,Google 可能會使用 Android 6.0 中的自動備份功能自動備份內部記憶體上的檔案。 內部記憶體有下列缺點:

  • 無法共享檔案。
  • 卸載應用程式時,將會刪除檔案。
  • 內部記憶體上可用的空間可能有限。

外部記憶體是指不是內部記憶體且無法獨佔存取應用程式的檔案記憶體。 外部記憶體的主要目的是提供一個位置,讓檔案在應用程式之間共用,或太大而無法放入內部記憶體。 外部記憶體的優點是,檔案的空間通常比內部記憶體多得多。 不過,外部記憶體不一定會出現在裝置上,而且可能需要使用者的特殊許可權才能存取它。

注意

針對支援多個用戶的裝置,Android 會在內部和外部記憶體上提供每個使用者自己的目錄。 裝置上的其他用戶無法存取此目錄。 只要應用程式不會將內部或外部記憶體上的檔案硬式編碼路徑,應用程式就看不見此區隔。

根據經驗法則,Xamarin.Android 應用程式應該偏好在合理的情況下將其檔案儲存在內部記憶體上,而且在需要與其他應用程式共用檔案時依賴外部記憶體、非常大,或即使卸載應用程式也一樣應該保留。 例如,組態檔最適合內部記憶體,因為除了建立內部記憶體的應用程式之外,它並不重要。 相反地,相片是外部存儲設備的良好候選專案。 它們可能非常大,而且在許多情況下,使用者可能會想要共用或存取它們,即使應用程式已卸載也一樣。

本指南將著重於內部記憶體。 如需在 Xamarin.Android 應用程式中使用外部記憶體的詳細資訊,請參閱外部記憶體指南

使用內部記憶體

應用程式的內部記憶體目錄是由作業系統所決定,並由屬性公開給Android應用程式 Android.Content.Context.FilesDir 。 這會傳回 Java.IO.File 物件,代表Android專用於應用程式的目錄。 例如,具有套件名稱 com.companyname 的應用程式,內部記憶體目錄可能是:

/data/user/0/com.companyname/files

本檔會將內部記憶體目錄 稱為INTERNAL_STORAGE

重要

內部記憶體目錄的確切路徑可能會因裝置而異,以及 Android 版本之間的不同。 因此,應用程式不得硬式編碼內部檔案記憶體目錄的路徑,而是改用 Xamarin.Android API,例如 System.Environment.GetFolderPath()

若要最大化程式代碼共用,Xamarin.Android 應用程式(或以 Xamarin.Android 為目標的 Xamarin.Forms 應用程式)應該使用 System.Environment.GetFolderPath() 方法。 在 Xamarin.Android 中,此方法會傳回與 相同位置 Android.Content.Context.FilesDir之目錄的字串。 這個方法會採用列舉 ,用來識別一組列舉常數, System.Environment.SpecialFolder代表操作系統所使用之特殊資料夾的路徑。 System.Environment.SpecialFolder並非所有值都會對應至 Xamarin.Android 上的有效目錄。 下表描述指定值 System.Environment.SpecialFolder的預期路徑:

System.Environment.SpecialFolder 路徑
ApplicationData INTERNAL_STORAGE/.config
Desktop INTERNAL_STORAGE/桌面
LocalApplicationData INTERNAL_STORAGE/.local/share
MyDocuments INTERNAL_STORAGE
MyMusic INTERNAL_STORAGE/音樂
MyPictures INTERNAL_STORAGE/圖片
MyVideos INTERNAL_STORAGE/影片
Personal INTERNAL_STORAGE
Fonts INTERNAL_STORAGE/.fonts
Templates INTERNAL_STORAGE/範本
CommonApplicationData /usr/share
CommonApplicationData /usr/share

讀取或寫入內部記憶體上的檔案

任何用於寫入檔案的 C# API 都已足夠;只需要取得配置給應用程式之目錄中檔案的路徑。 強烈建議使用 .NET API 的異步版本,將可能與封鎖主線程之檔案存取相關聯的任何問題降到最低。

此代碼段是將整數寫入 UTF-8 文字檔至應用程式內部儲存目錄的其中一個範例:

public async Task SaveCountAsync(int count)
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");
    using (var writer = File.CreateText(backingFile))
    {
        await writer.WriteLineAsync(count.ToString());
    }
}

下一個代碼段提供一種方式來讀取儲存在文字檔中的整數值:

public async Task<int> ReadCountAsync()
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");

    if (backingFile == null || !File.Exists(backingFile))
    {
        return 0;
    }

    var count = 0;
    using (var reader = new StreamReader(backingFile, true))
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (int.TryParse(line, out var newcount))
            {
                count = newcount;
            }
        }
    }

    return count;
}

使用 Xamarin.Essentials – 文件系統協助程式

Xamarin.Essentials 是一組 API,可用於撰寫跨平臺相容程序代碼。 檔案系統協助程式是一個類別,其中包含一系列協助程式,可簡化尋找應用程式的快取和數據目錄。 此代碼段提供如何尋找應用程式的內部記憶體目錄和快取目錄的範例:

// Get the path to a file on internal storage
var backingFile = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "count.txt");

// Get the path to a file in the cache directory
var cacheFile = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, "count.txt");

從隱藏檔案 MediaStore

MediaStore是 Android 元件,可收集 Android 裝置上媒體檔案(影片、音樂、影像)的相關元數據。 其用途是簡化這些檔案在裝置上所有 Android 應用程式的共用。

私人檔案不會顯示為可共享媒體。 例如,如果應用程式將圖片儲存至其私人外部記憶體,媒體掃描器將不會挑選該檔案。MediaStore

公用檔案將會由 MediaStore挑選。 具有零位元組檔名的 目錄。NOMEDIA 會不會掃描 MediaStore