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 修改或刪除媒體檔案。

匯入現有的單張圖片

匯入現有的單張圖片 (例如用來做為使用者個人資料相片) 時,應用程式可以透過自己的使用者介面進行這項作業,也可以使用系統挑選器。

提供您自己的使用者介面

請使用以下方法:

  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 直接存取檔案路徑。

在舊版本中執行

使用儲存空間存取架構,讓使用者選取可供您應用程式寫入檔案的次要儲存空間磁碟區位置。

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

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

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

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

如果應用程式指定 Android 11 版本為目標
  1. preserveLegacyExternalStorage 標記設為 true保留舊版儲存空間模型,如此一來,當使用者升級您的應用程式,指定 Android 11 為目標版本時,應用程式就可以遷移使用者的資料。

  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) 及以上版本中,這些位置均已加密。這些位置非常適合儲存僅限應用程式本身存取的機密資料。

外部儲存空間目錄

如果內部儲存空間不足,無法儲存應用程式特定檔案,請考慮改用外部儲存空間。雖然其他應用程式在擁有適當權限的情況下,可以存取這些目錄,但這些目錄中儲存的檔案僅供您的應用程式使用。

在 Android 4.4 (API 級別 19) 以上版本中,您的應用程式無須請求任何儲存空間相關權限,即可存取外部儲存空間內的應用程式專屬目錄。

使用者解除安裝應用程式時,系統會移除儲存在應用程式特定儲存空間中的檔案,因此您不應使用此儲存空間儲存任何使用者預期可獨立於應用程式保留的內容。

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

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

在測試中停用限定範圍儲存空間功能

根據預設,在 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 儲存空間,請參閱下列資料:

網誌文章