應用中關於資料的持久化儲存,不管是簡單的SharedPreferences還是資料庫SQLiteDatabase,本質上都是將資料儲存到系統的某種型別的檔案中。因此可以直接使用java.io.File檔案類將資料以任意型別存取。

在獲取到File檔案類的物件後,就可以使用其相關方法執行對檔案的讀寫等相關操作,這部分屬於Java語言開發或Kotlin語言開發的基礎知識,不再多言。而在Android系統上因為各種原因,獲取File物件的方式有所區別,本文將重點介紹。

這裡需要注意,File檔案類不僅可以是一個可讀寫資料的正常檔案,也可以是一個包含上述檔案的資料夾。下文如無特別說明,檔案亦指儲存資料的正常檔案和包含正常檔案的資料夾。

硬體儲存區域

在瞭解資料的檔案儲存之前,當然要先搞清楚Android系統對硬體儲存裝置的劃分規則。

一般裝置所安裝Android系統的分割槽空間,被定義為Android系統的內部儲存空間。通常應用程式的安裝包及安裝後的相關資料檔案,都預設儲存在內部儲存空間中。內部儲存空間為不同應用程式劃分了不同的訪問區間可以操作,而系統使用者是無法正常訪問內部儲存空間的,這有效防止了應用程式間的檔案安全性。

內部儲存的空間往往不會太大,因此不建議應用程式在內部儲存中佔用大量空間。於是Android系統又定義了外部儲存空間,應用程式過大的檔案可儲存在外部儲存中,比如相機應用程式所儲存的照片。同時外部儲存是允許系統使用者訪問的,這也就是檔案管理應用程式所展示的儲存空間。

而在某些硬體裝置中,比如較老版本的移動手機,為了增大硬體儲存空間,還會使用SDcard增加可擴充套件儲存區。這部分儲存同樣屬於系統的外部儲存空間範疇。

在訪問外部儲存時,應用程式需要申請外部儲存的讀寫許可權,包括android.Manifest.permission.READ_EXTERNAL_STORAGEandroid.Manifest.permission.WRITE_EXTERNAL_STORAGE

系統級檔案

所謂系統級檔案,就是該類檔案允許系統中的任何應用程式讀寫訪問。其中外部儲存空間中的檔案都是系統級檔案。

目標版本級別小於29的應用程式

在Android10即API 29以前,手機的外接SD卡可以作為系統級檔案使用,在應用程式中,可以藉助android.os.Environment環境類的getExternalStorageDirectory()系列靜態方法獲取外部儲存中的相關File檔案,同時使用isExternalStorageEmulated ()系列靜態方法獲取類似外部儲存是否載入等資訊。

這裡獲取外部儲存根目錄的Environment.getExternalStorageDirectory()方法在API29中廢棄,而在API30中替換為Environment.getStorageDirectory()方法。

目標版本級別等於29的應用程式

從Android10開始,系統引入了分割槽儲存的概念,在開啟了分割槽儲存的應用程式中,只能訪問其應用級檔案和媒體檔案。應用級檔案在下文會有詳細講解。而所謂的媒體檔案是儲存在外部儲存中的,主要包括圖片、音訊、視訊和下載檔案,共四種媒體檔案類。

其中一般圖片儲存路徑為 DCIM/Picture/ ,並將檔案資訊儲存在MediaStore.Images類定義的相關常量所標記的資料庫中;

音訊儲存路徑為 Alarms/Audiobooks/Music/Notifications/Podcasts/Ringtones/ ,並將檔案資訊儲存在MediaStore.Audio類定義的相關常量所標記的資料庫中;

視訊儲存路徑為 DCIM/Movies/Picture/ ,並將檔案資訊儲存在MediaStore.Video類定義的相關常量所標記的資料庫中;

下載檔案儲存路徑為 Download/ ,並將檔案資訊儲存在MediaStore.Downloads類定義的相關常量所標記的資料庫中。

這裡的下載檔案路徑MediaStore.Downloads需要特別注意,其路徑只適用於Android10即API 29及以上的系統上,不管應用程式的目標版本級別為多少,只要在Android9及以下版本中,就無法訪問下載檔案路徑的相關內容。

而且在下載檔案路徑中的檔案,如果由非建立該檔案的應用程式所訪問,只能使用儲存訪問框架呼叫系統檔案選擇器與使用者互動,而不能像另外三種媒體檔案一樣直接程式碼訪問。

分割槽儲存的開啟方式是修改 AndroidManifest.xml 清單檔案,在 <application> 標籤中增加屬性值內容 android:requestLegacyExternalStorage=“false”,這也是預設設定;反之,如果設定android:requestLegacyExternalStorage=“true”則是關閉分割槽儲存,官方推薦該配置僅可在檔案相容性適配階段作為過渡使用。

開啟了分割槽儲存的應用程式,在讀寫自己的應用級檔案時,不需要另外申請許可權。但是在訪問媒體檔案時,同樣需要外部儲存的讀寫許可權,包括android.Manifest.permission.READ_EXTERNAL_STORAGEandroid.Manifest.permission.WRITE_EXTERNAL_STORAGE

而媒體檔案的訪問方式,是通過上下文環境Context物件的getContentResolver()方法,獲取android.content.ContentResolver內容解析類物件,通過該物件可以對Android系統的資料庫進行增刪改查等操作,其用到的相關常量都可以在上文的四種媒體檔案類中查詢。至於ContentResolver內容解析類的原理和使用方式,將在後面關於應用級檔案共享的文章中詳細講解。

目標版本級別不小於30的應用程式

在Android11及API 30及以上的系統中,強制開啟了分割槽儲存,應用只能訪問其應用級檔案和媒體檔案。同時新增了管理外部儲存的許可權android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,與上文的外部儲存的讀寫許可權共同授權,允許當前應用程式訪問外部儲存的所有檔案。

而訪問外部儲存的方式同樣是使用Environment環境類的getStorageDirectory()系列靜態方法獲取外部儲存中的相關File檔案。

應用級檔案

所謂應用級檔案,就是該檔案只執行其所屬的應用程式讀寫訪問,只能由該應用程式建立,且隨著應用程式的解除安裝或清除資料而刪除。

在沒有開啟分割槽儲存之前,即Android 10系統版本之前,應用級檔案不僅可以儲存在內部儲存中,也可以儲存在外部儲存中,且由於應用程式之間對外部儲存的訪問並未受到限制,所以外部儲存部分的應用級檔案往往可以被不同應用程式訪問。

而自Android 10啟用分割槽儲存之後,應用程式的應用級檔案只能儲存在內部儲存中,即便在Android 11系統之後恢復了應用程式對外部儲存的訪問授權,也不會在外部儲存中建立應用級檔案。

訪問應用級檔案的方式是通過上下文環境類

android.content.Context物件。

呼叫Context物件的getDir(String name, int mode)方法可以獲取指定目錄下的私有檔案,返回File檔案型別的物件,通常路徑為 /data/data/應用包名/app_name/name。其中引數 name 是指定的檔名;引數 mode 是對該檔案的操作模式,通常為Context.MODE_PRIVATE=0表示檔案私有,或者Context.MODE_APPEND為寫入檔案的追加模式。

呼叫Context物件的getCacheDir()方法可以獲取當前應用程式下的快取檔案目錄,返回File檔案型別的物件,通常路徑為 /data/data/應用包名/cache/。

呼叫Context物件的getFilesDir()方法可以獲取當前應用程式下的特定檔案目錄,返回File檔案型別的物件,通常路徑為 /data/data/應用包名/files/。

還有其他相關方法,可以獲取當前應用程式下的某些指定檔案目錄,不再贅述。


對於應用程式下的應用級檔案通常是不允許其他應用訪問的,可如果某些應用級檔案的確想被其他應用程式所訪問,比如某個通訊類應用程式需要訪問通訊錄應用中的聯絡人資訊,有什麼好的辦法呢?敬請關注下一章瞭解詳情。