Android 儲存空間用途和最佳做法

為了讓使用者進一步控管檔案,以及避免檔案過於雜亂,Android 10 針對應用程式推出名為「限定範圍儲存空間」的全新儲存範本。限定範圍儲存空間改變了應用程式在外部儲存空間儲存及存取檔案的方式。為協助您遷移應用程式以支援限定範圍儲存空間,請按照本指南中的「常見儲存空間用途最佳做法」。用途可分為兩種:處理媒體檔案處理非媒體檔案

如要進一步瞭解如何儲存及存取 Android 裝置上的檔案,請參閱「儲存空間訓練指南」

處理媒體檔案

本節說明處理媒體檔案 (影片、圖片和音訊檔案) 的幾個常見用途,以及應用程式可使用的高階方法。下表概述這些用途,以及各部分詳細說明的連結。

用途 摘要
顯示所有圖片或影片檔 所有 Android 版本皆使用相同方法。
顯示特定資料夾中的圖片或影片 所有 Android 版本皆使用相同方法。
存取相片的位置資訊 如果應用程式使用限定範圍儲存空間,請採用其中一種方法。如果應用程式不使用限定範圍儲存空間,請使用其他方法。
決定新下載內容的儲存位置 如果應用程式使用限定範圍儲存空間,請採用其中一種方法。如果應用程式不使用限定範圍儲存空間,請使用其他方法。
匯出使用者媒體檔案至裝置 所有 Android 版本皆使用相同方法。
一次修改或刪除多個媒體檔案 如使用 Android 11 版本,請選用一種方法。如使用 Android 10 版本,請停用限定範圍儲存空間,並改用 Android 9 及以下版本的方法。
匯入已存在的單張圖片 所有 Android 版本皆使用相同方法。
拍攝單一圖片 所有 Android 版本皆使用相同方法。
與其他應用程式共用媒體檔案 所有 Android 版本皆使用相同方法。
與特定應用程式共用媒體檔案 所有 Android 版本皆使用相同方法。
使用直接檔案路徑的程式碼或程式庫存取檔案 如使用 Android 11 版本,請選用一種方法。如使用 Android 10 版本,請停用限定範圍儲存空間,並改用 Android 9 及以下版本的方法。

顯示多個資料夾中的圖片或影片

請使用 query() API 查詢媒體集合。如要篩選或排序媒體檔案,請調整 projectionselectionselectionArgssortOrder 參數。

顯示特定資料夾中的圖片或影片

請使用這個方法:

  1. 請按照「要求應用程式權限」列出的最佳做法,以要求 READ_EXTERNAL_STORAGE 權限。
  2. 請根據 MediaColumns.DATA 的值擷取媒體檔案,該檔案含有存取磁碟機媒體檔案的絕對檔案系統路徑。

注意:如要存取現有媒體檔案,則使用邏輯中的 DATA 欄位的值。因為這個值包含有效的檔案路徑。但是,請勿以為可隨時存取檔案。請妥善處理任何可能發生的檔案 I/O 錯誤。

另一方面,如要建立或更新媒體檔案,請勿使用 DATA 欄。請改用 DISPLAY_NAME 欄和 RELATIVE_PATH 欄。

存取相片的位置資訊

如果應用程式使用限定範圍儲存空間功能,請按照媒體儲存空間指南中,「相片的位置資訊」所述的步驟操作。

決定新下載內容的儲存位置

如果應用程式使用限定範圍儲存空間,請注意您選擇下載媒體檔案的儲存位置。

如果其他應用程式需要存取檔案,建議您使用定義明確的媒體集合處理下載內容或文件集合。

在 Android 11 及以上版本中,即使您使用 DownloadManager 擷取這些檔案,外部儲存空間應用程式專屬目錄的檔案也無法供其他應用程式存取。

匯出使用者媒體檔案至裝置

請定義適當的預設位置以儲存使用者媒體檔案:

在單一作業中修改或刪除多個媒體檔案

請根據執行應用程式的裝置所搭載的 Android 版本加入邏輯。

裝置版本為 Android 11

請使用這個方法:

  1. 使用 MediaStore.createWriteRequest()MediaStore.createTrashRequest() 建立待處理意圖,讓應用程式寫入或刪除要求,然後提示使用者授權,以叫用此意圖來編輯檔案。
  2. 評估使用者回應:

    • 如果使用者授予權限,請繼續執行修改或刪除作業。
    • 如果使用者未授予權限,請向使用者說明這項功能需要權限的原因。

進一步瞭解在 Android 11 及以上版本的裝置中,有哪些方法可管理媒體檔案群組

裝置版本為 Android 10

如果您的應用程式以 Android 10 (API 級別 29) 為目標版本,請停用限定範圍儲存空間,並繼續使用 Android 9 及以下版本的方法來進行管理。

裝置版本為 Android 9 或以下版本

請使用這個方法:

  1. 請按照「要求應用程式權限」列出的最佳做法,以要求 WRITE_EXTERNAL_STORAGE 權限。
  2. 使用 MediaStore API 修改或刪除媒體檔案。

匯入已存在的單張圖片

如要匯入現有單一圖片,以用於使用者個人資料相片這類用途,應用程式可以使用自己的 UI 操作,或是使用系統挑選器。

呈現個人風格的使用者介面

請使用這個方法:

  1. 請按照「要求應用程式權限」列出的最佳做法,以要求 READ_EXTERNAL_STORAGE 權限。
  2. 請使用 query() API 查詢媒體集合
  3. 請使用應用程式自訂使用者介面以顯示結果。

使用系統挑選器

使用 ACTION_GET_CONTENT 意圖,應用程式會要求使用者選擇要匯入的圖片。

如要過濾系統挑選器向使用者顯示的圖片類型,可以使用 setType()EXTRA_MIME_TYPES

拍攝單一圖片

如果您想要拍攝單一圖片供應用程式使用 (例如作為使用者個人資料的相片),請使用 ACTION_IMAGE_CAPTURE 意圖,要求使用者採用裝置相機拍照。系統會將拍攝完成的相片儲存在 MediaStore.Images 資料表中。

與其他應用程式共用媒體檔案

如要直接新增記錄至 MediaStore,請使用 insert() 方法。詳情請參閱媒體儲存指南的「新增項目」一節。

與特定應用程式共用媒體檔案

請使用「設定檔案共用」指南中所述的 Android FileProvider 元件。

從使用直接檔案路徑的程式碼或程式庫存取檔案

請根據執行應用程式的裝置所搭載的 Android 版本加入邏輯。

裝置版本為 Android 11

請使用這個方法:

  1. 請按照「要求應用程式權限」列出的最佳做法,以要求 READ_EXTERNAL_STORAGE 權限。
  2. 使用直接檔案路徑存取檔案。

要瞭解如何使用直接檔案路徑開啟媒體檔案,詳情請參閱相關指南。

裝置版本為 Android 10

如果您的應用程式以 Android 10 (API 級別 29) 為目標版本,請停用限定範圍儲存空間,並繼續使用 Android 9 及以下版本的方法來進行管理。

裝置版本為 Android 9 或以下版本

請使用這個方法:

  1. 請按照「要求應用程式權限」列出的最佳做法,以要求 WRITE_EXTERNAL_STORAGE 權限。
  2. 使用直接檔案路徑存取檔案。

處理非媒體檔案

本節將說明處理非媒體檔案的幾個常見用途,以及應用程式可以使用的高階方法。下表概述這些用途,以及各部分詳細說明的連結。

用途 摘要
開啟文件檔 所有 Android 版本皆使用相同方法。
將檔案寫入次要儲存磁碟區 如使用 Android 11 版本,請選用一種方法。對先前的 Android 版本,請使用不同的方法。
從舊版儲存空間位置遷移現有檔案 請盡可能將檔案遷移至限定範圍儲存空間。針對 Android 10,可視需要停用限定範圍儲存空間。
與其他應用程式共用內容 所有 Android 版本皆使用相同方法。
快取非媒體檔案 所有 Android 版本皆使用相同方法。
匯出非媒體檔案至裝置 如果應用程式使用限定範圍儲存空間,請採用其中一種方法。如果應用程式不使用限定範圍儲存空間,請使用其他方法。

開啟文件檔

ACTION_OPEN_DOCUMENT 意圖會要求使用者透過系統挑選器來開啟檔案。如要過濾挑選器提供給使用者的檔案類型,即可用 setType()EXTRA_MIME_TYPES

舉例來說,只要使用以下程式碼,即可找出所有 PDF、ODT 和 TXT 檔案:

Kotlin

startActivityForResult(
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
                    "application/pdf", // .pdf
                    "application/vnd.oasis.opendocument.text", // .odt
                    "text/plain" // .txt
            ))
        },
        REQUEST_CODE
      )

Java

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                "application/pdf", // .pdf
                "application/vnd.oasis.opendocument.text", // .odt
                "text/plain" // .txt
        });
        startActivityForResult(intent, REQUEST_CODE);

寫入次要儲存磁碟區的檔案

次要儲存磁碟區包含 SD 卡。只要使用 StorageVolume 類別,即可存取特定儲存磁碟區的資訊。

請根據執行應用程式的裝置所搭載的 Android 版本加入邏輯。

裝置版本為 Android 11

請使用這個方法:

  1. 使用限定範圍儲存空間模型。
  2. 目標版本為 Android 10 (API 級別 29) 或以下。
  3. 宣告 WRITE_EXTERNAL_STORAGE 權限。
  4. 執行以下任一類型的存取權:
    • 使用 MediaStore API 存取檔案。
    • 使用 Filefopen() 等 API 直接存取檔案路徑。

在舊版執行

如要使用者在次要儲存磁碟區,選取應用程式可以寫入檔案的位置,請使用 Storage Access Framework

從舊版儲存位置遷移現有檔案

如果該目錄不是應用程式專屬目錄或公開共用目錄,則系統會將其視為舊版儲存空間位置。如果您的應用程式在舊版儲存空間位置建立或取用檔案,建議您將應用程式的檔案,遷移至限定範圍儲存空間可存取的位置,並對應用程式進行必要的變更,以使用限定範圍儲存空間中的檔案。

保留舊版儲存位置存取權以遷移資料

您的應用程式需保留舊版儲存位置的存取權,才能將其他應用程式檔案,遷移至可存取的限定範圍儲存空間。請根據應用程式的目標 API 級別決定要採用的方法。

以 Android 11 為目標版本的應用程式
  1. 如果您的應用程式以 Android 11 為目標版本,只要將 preserveLegacyExternalStorage 標記設為 true,即可保留舊版儲存空間模型,那麼升級時就能遷移使用者資料。

  2. 如果是搭載 Android 10 的裝置,請繼續停用限定範圍儲存空間,以便應用程式繼續存取舊版儲存位置的檔案。

以 Android 10 為目標版本的應用程式

只要停用限定範圍儲存空間,應用程式即可輕鬆在各 Android 版本中保持正常運作。

遷移應用程式資料

請使用下列方法遷移應用程式:

  1. 目標版本為 Android 10 以下版本。
  2. 停用限定範圍儲存空間,讓應用程式能夠存取需要遷移的檔案。
  3. 請編寫使用 File API 的程式碼,將現有檔案從 /sdcard/ 下的目前位置,移至限定範圍儲存空間可存取的位置:

    1. 將任何私人應用程式檔案移至 getExternalFilesDir() 方法傳回的目錄。
    2. 將所有共用的非媒體檔案移至 Downloads/ 目錄的應用程式專屬子目錄。
  4. /sdcard/ 目錄中移除應用程式的舊版儲存空間目錄。

安裝新版應用程式後,必須完成裝置中的資料遷移程序。開發人員可以建立數據分析事件,監控所有使用者數量的遷移程序。

資料遷移完成後,請發布更新版本的應用程式,也就是指定 Android 11 為目標版本的應用程式。

與其他應用程式分享內容

如要與其他應用程式共用檔案,請使用 FileProvider。對於需要共用檔案的應用程式,建議針對各應用程式分別使用內容供應器,接著將應用程式加入集合,同時處理資料同步。

快取非媒體檔案

請根據檔案類型決定快取方法。

匯出非媒體檔案至裝置

定義適當的預設位置來儲存非媒體檔案。如要將應用程式專屬目錄中的檔案匯出至一般的存取位置,請使用 MediaStore 下載內容或文件集合,將非媒體檔案匯出至裝置。

暫時停用限定範圍儲存空間

如應用程式與限定範圍儲存空間尚未完全相容,在測試正式版應用程式中,開發人員可以暫時停用此功能。

退出測試

在 Android 10 (API 級別 29) 及以上版本中,根據預設,應用程式的測試會在儲存空間沙箱中執行。沙箱可避免應用程式存取應用程式專屬目錄和公開共用目錄以外的檔案。

如果測試輸出檔案給主機 (例如螢幕截圖、偵錯資料、涵蓋率資料或成效指標),開發人員可以將檔案寫入全域目錄。只要將下列標記新增至叫用 am instrument 的相關控管工具即可:

-e no-isolated-storage 1

這個標記不只影響檢測設備測試案例的所有行為,也會影響所有叫用的測試程式碼。因此,一旦使用這個標記,就無法驗證應用程式與限定範圍儲存空間的相容性。針對測試輸出內容,建議將標記改寫入殼層可讀取的應用程式限定儲存空間。那麼開發人員就可以取得該限定空間中的目錄。如要決定取得哪個目錄,請呼叫 getExternalMediaDirs()

退出正式版應用程式

如果您的應用程式以 Android 10 (API 級別 29) 或以下為目標版本,即可在正式版應用程式中,暫時停用限定範圍儲存空間。不過,如果目標版本是 Android 10,則必須在應用程式的資訊清單檔案中將 requestLegacyExternalStorage 的值設為 true

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

如要測試啟用限定範圍儲存空間時,目標版本為 Android 10 或以下的應用程式會有什麼行為,開發人員可將 requestLegacyExternalStorage 的值設為 false。如果使用搭載 Android 11 的裝置執行測試,也可以使用應用程式相容性標記,測試應用程式在啟用/停用限定範圍儲存空間時的行為。

其他資源

如要進一步瞭解 Android 儲存空間,請參閱下列資料:

網誌文章