【版權申明】非商業目的可自由轉載
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/102684695
出自:shusheng007
前言
現在大家都在說:數據就是未來世界的黃金,其實個人覺得應該說被處理過的數據纔是,而這個處理技術就被我們親切的稱呼爲大數據,就是在海量數據中淘金。
在Android的日常開發過程中,我們經常會遇到將數據存儲到本地的需求,但做了很多這樣的工作,卻不瞭解各種存儲方式的適用場景,導致各種安全及性能問題,我自己最開始也有這方面的問題。
選擇存儲方式時要考慮:數據的尺寸、類型、特徵、安全性等問題,下面記錄一下目前android系統提供的各種存儲方式。
文件存儲系統
Android的文件系統繼承至Linux系統。早期的劃分更側向於物理劃分,即可卸載的外部存儲,例如外插sd卡,U盤等,不可卸載的爲內部存儲。發展到目前,這個概念已經淡化了,現在主流設備都沒有sd卡插槽了,但是設備的存儲仍然被劃分爲內部與外部。現在內外存儲的最大區別集中在讀寫權限上。
內部存儲系統
Android會爲每個App分配一塊私有的存儲空間,存儲在這裏的數據只有此App自己可以訪問到,用戶及其他App均不可訪問,被root的設備除外。
特點:
- 數據隨着App的卸載而自動刪除
- 不需要申請存儲讀寫權限
- 數據私有,除自己外不可見
內部存儲又分爲一個持久目錄與一個緩存目錄。當設備存儲不足時,系統會刪除緩存目錄的數據以釋放空間,而且不會通知App,因此在使用緩存中的數據時,原則上應該先檢查其狀態。爲了節約空間,我們應該限制緩存目錄的空間,例如2M
, 然後定期主動清除。
獲取內部存儲目錄
getFilesDir()
在我的Mix2上的路徑爲
/data/user/0/app的包名/files
獲取內部存儲緩存目錄
getCacheDir()
在我的Mix2上的路徑爲
/data/user/0/app的包名/cache
外部存儲系統
外部存儲就比較複雜了,最初的Android設備外部存儲就是指外部接入的存儲,例如sd卡,U盤,硬盤。現在除了被操作系統劃分爲內部存儲的部分,其餘的都叫外部存儲。
外部存儲甚至可以使用內部存儲模擬,所以說外部還是內部就是系統根據一些標準人爲劃分的。
adb shell sm set-virtual-disk true
外部存儲根據功能又可以進一步分爲公開文件目錄與私有文件目錄
公開文件
指主觀上希望App產生的數據可以供其他app訪問,例如相機拍攝的照片希望讓別的App使用,瀏覽器下載的文件也希望讓其他App訪問,即使是在用戶卸載了這些App之後。
此類文件Google推薦使用如下兩種方式處理
上面兩種方式均是利用content provider
來進行交互,有時間再詳細寫一下關於這兩個方面的內容。
其實還有一種Android10以後不推薦文件目錄,就是設備存儲的根目錄,但是要注意,訪問這些目錄是需要存儲讀寫權限的。
獲取根目錄(Android 10 以後廢棄)
Environment.getExternalStorageDirectory()
在Mix2的目錄爲
/storage/emulated/0
另一種獲取獲取根目錄下一級目錄的方法爲
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
在Mix2的目錄爲
/storage/emulated/0/Pictures
注意那個入參,Environment.DIRECTORY_PICTURES
就是“Pictures”,我們也可以傳入其他的參數,例如
public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_SCREENSHOTS = "Screenshots";
第二種方式可以看做的第一種方式的特例,如果你想要在根目錄下面建立一個你自己的文件夾,那就需要使用第一種方式了。
特點:
- 需要存儲讀寫權限
- 不跟隨App的卸載而刪除
- 容量巨大
- 不可靠,使用時需要確保存儲可用
私有文件
指主觀上不希望App產生的數據被別的App訪問。
是一個包含App包名的路徑,與內部存儲一樣,存在一個持久目錄和一個緩存目錄
獲取外部存儲目錄
getExternalFilesDir(String type)
當參數傳入null時,在我的Mix2上的路徑爲
/storage/emulated/0/Android/data/app的包名/files
入參爲子目錄,如果傳入“Pictures”,那麼就會在上面的目錄下面增加一級“/Pictures”
獲取外部存儲緩存目錄
getExternalCacheDir()
在我的Mix2上的路徑爲
/storage/emulated/0/Android/data/app的包名/cache
一般情況下,我應該將APP的非敏感數據存放在此處。
特點:
- Android 4.4 (API level 19)之後不需要存儲讀寫權限
- 跟隨App的卸載而刪除
- 技術上仍然是全局可訪問的,只要某個應用知道具體文件的目錄,且具有讀寫存儲的權限
將圖片插入相冊中
有時我們會有在相冊中顯示APP保存的圖片這樣的需求,那如何做呢?
思路:使用 'MediaScannerConnection’將要展示的圖片掃描一下,讓系統生成一個代表這張圖片的Uri並插入一個系統維護的列表中,相冊會去讀取這個Uri然後展示。
下面的代碼的功能爲:保存一張圖片並掃描到媒體庫中。打開系統的相冊後,會看到我們保存的照片
private fun saveImageOfRaw(resId: Int) {
val inputStream = resources.openRawResource(resId)
//路徑必須是存儲在外部存儲的非私有路徑下才能在相冊裏面展示
// val dir = [email protected](Environment.DIRECTORY_PICTURES) 不可以
// val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 可以
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val dirPath = Environment.getExternalStorageDirectory().path
val dir = File("$dirPath/AndroidDevMemo")
if (dir.exists().not()) {
dir.mkdir()
}
val file = File(dir, "mySon.jpg")
val data = ByteArray(inputStream.available())
inputStream.read(data)
FileOutputStream(file).use {
it.write(data)
}
MediaScannerConnection.scanFile(this@StorageActivity, arrayOf(file.toString()), null,
object : MediaScannerConnection.OnScanCompletedListener {
override fun onScanCompleted(path: String?, uri: Uri?) {
printResult("Scanned $path:->uri=$uri\n\n")
}
})
}
圖片的路徑爲:
/storage/emulated/0/AndroidDevMemo/mySon.jpg
掃描後生成的Uri
爲:
content://meida/external/images/meida/44152
值得一提的是,當圖片的存儲目錄是私有文件目錄時,無法顯示在相冊中,但是可以生成Uri
,需要手動保存到媒體庫中吧。
請參考MediaStore
SharedPreferences
原理
底層爲一個xml文件,存儲路徑隨着不同的設備可能會不一致,此處給出獲取其路徑的方法
val dataPath= getDatabasePath("app包名_preferences.xml").absolutePath
上面是獲取默認文件的方式,如果自己定義了shared preferences 文件的名稱,則傳入自定義名稱。
SharedPreferences 主要用於存儲鍵值對
使用方法
- 獲取一個SharedPreferences 實例
一般會生成一個APP共享的實例val sharePreferences = getSharedPreferences("$PACKAGE_ID _preference", Context.MODE_PRIVATE)
- 保存數據
保存數據有同步和異步兩種方式,不過每一種方式都是先獲取一個Eidtor
,然後壓入要保存的數據,然後提交.
同步方式使用commit()
,異步方式使用apply()
with(sharePreferences.edit()) { putString("name", "ShuSheng007") commit() //commit() 同步寫入 //apply() 異步寫入 }
- 提取數據
val result = sharePreferences.getString("name", "")
數據庫 (sqlite)
Android 支持 sqlite 數據庫,而且提供了一套使用sqlite數據的Api,但是目前 google 推薦使用 Room這個庫來代替直接操sqlite數據庫Api.
概述
好知識需要及時總結,積極傳播,自己就是從一個什麼都不懂的人自學過來的,深知隨便一個小問題對於入門的人來說就是一座大山,希望本文可以給翻山的同學一點幫助,也是對自己過往的一個記錄,也許幾年後咱就離開這個行業了。
不知不覺10月也過去了,突然感覺自己的人生活成了那種 “20歲就死了,80歲才埋”的樣子了,對未來沒有了興奮感,沒有了期待。有人說,如果感到迷惘就去讀書和健身,我決定試一試,嘗試做一些打破日復一日的常規的事情。。。