Android系統編程入門系列之應用數據文件化保存

應用中關於數據的持久化保存,不管是簡單的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/。
還有其他相關方法,可以獲取當前應用程序下的某些指定文件目錄,不再贅述。


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