存儲路徑及演化
首先看這張文件從Android文件存儲使用參考轉(zhuǎn)載的存儲結(jié)構(gòu)圖沽讹,里面明確了通過各種Android接口獲取到的文件路徑程奠。
($rootDir)
+- /data -> Environment.getDataDirectory()
| |
| | ($appDataDir)
| +- data/com.srain.cube.sample
| |
| | ($filesDir)
| +- files -> Context.getFilesDir() / Context.getFileStreamPath("")
| | |
| | +- file1 -> Context.getFileStreamPath("file1")
| | ($cacheDir)
| +- cache -> Context.getCacheDir()
| |
| +- app_$name ->(Context.getDir(String name, int mode)
|
| ($rootDir)
+- /storage/sdcard0 -> Environment.getExternalStorageDirectory()
| / Environment.getExternalStoragePublicDirectory("")
|
+- dir1 -> Environment.getExternalStoragePublicDirectory("dir1")
|
| ($appDataDir)
+- Andorid/data/com.srain.cube.sample
|
| ($filesDir)
+- files -> Context.getExternalFilesDir("")
| |
| +- file1 -> Context.getExternalFilesDir("file1")
| +- Music -> Context.getExternalFilesDir(Environment.Music);
| +- Picture -> ... Environment.Picture
| +- ...
|
| ($cacheDir)
+- cache -> Context.getExternalCacheDir()
|
+- ???
隨著Android系統(tǒng)版本的演進(jìn)侧戴,文件存儲系統(tǒng)也在變化车柠。
- 遠(yuǎn)古時代:系統(tǒng)存儲和外部存儲是物理分隔的。沒有SD卡的手機(jī)甚至無法使用照相機(jī)功能骡尽。這時外部存儲的路徑是
/sdcard
遣妥。 - 4.0:系統(tǒng)存儲空間開始變大,因此在galaxy nexus手機(jī)上userdata分區(qū)很大攀细,被掛在
/data
目錄箫踩。為了擺脫對SD卡的依賴,google想了一個辦法谭贪。userdata分區(qū)下有個目錄叫media境钟,是內(nèi)置sd卡的數(shù)據(jù)存儲位置,使用fuse技術(shù)將/data/media
虛擬成為一個叫做/dev/fuse
的設(shè)備俭识,被掛載在/mnt/sdcard
目錄下慨削。為了兼容老的應(yīng)用程序,又創(chuàng)建了一個指向/mnt/sdcard
的軟引用/sdcard
。 - 4.1:
/dev/fuse
同時被掛載到/storage/sdcard0
缚态,這個sdcard0表示第一個sd卡磁椒。如果有外置sd卡,那會多一個/storage/sdcard1
玫芦。上面的外部存儲$rootDir就是指4.1系統(tǒng)上的/storage/sdcard0
浆熔,不同的系統(tǒng)版本上不盡相同。 - 4.2:
/dev/fuse
被掛載到/storage/emulated/0
桥帆。為了兼容以前的命名同時也掛載到/storage/emulated/legacy
蘸拔,并建立三個軟連接指向它。(/storage/sdcard0
环葵,/sdcard
调窍,/mnt/sdcard
)
因此/dev/fuse雖然實(shí)際上是內(nèi)置SD卡的一部分,但已變成了用戶可直接操作的部分了张遭,因此屬于外部存儲邓萨。文件結(jié)構(gòu)圖的下半部分,/dev/fuse的掛載點(diǎn)就是Android外部存儲存儲的根目錄{rootDir}
應(yīng)用數(shù)據(jù)目錄
根據(jù)上圖可以看到菊卷,應(yīng)用數(shù)據(jù)的根目錄為
- 內(nèi)部:/data/data/package.name/
- 外部:{rootDir}/Android/data/package.name
如需保證數(shù)據(jù)的安全性缔恳,一定要將其保存在內(nèi)部存儲中。
在這些目錄下的數(shù)據(jù)洁闰,可以在由用戶在系統(tǒng)設(shè)置中清除歉甚,在app卸載之后,也會被系統(tǒng)自動清理扑眉。因此纸泄,我們應(yīng)將應(yīng)用的數(shù)據(jù)放于這兩個目錄中。
這里注意:系統(tǒng)設(shè)置中顯示的應(yīng)用占用空間腰素,并不包括通過Context#getExternalXXX
獲取目錄中文件所占據(jù)的空間聘裁,但在清理時,卻會包括這部分弓千。
緩存數(shù)據(jù)
如果數(shù)據(jù)在不知情的情況下被刪除衡便,會導(dǎo)致程序運(yùn)行或用戶使用的異常,那么這部分?jǐn)?shù)據(jù)一定是不是數(shù)據(jù)洋访。例如視頻軟件中已經(jīng)下載的離線視頻镣陕。
反之,則是緩存數(shù)據(jù)姻政。例如為了節(jié)省流量呆抑,而暫時保存在手機(jī)上的網(wǎng)絡(luò)圖片。
可以看到和緩存相關(guān)的文件夾有兩個Context#getCacheDir()
和Context#getExternalCacheDir()
系統(tǒng)會在機(jī)器容量緊張的時候刪除這部分文件扶歪,以保證系統(tǒng)的流程運(yùn)行理肺。然而系統(tǒng)并不會保證何時刪除這些文件堰氓,因此使用時還是需要對這部分文件的大小設(shè)定一個上限蜓堕。
自動清理緩存文件的機(jī)制對于Context#getExternalCacheDir()
不一定有效,僅在滿足以下兩個條件時才生效标捺。
- 4.2及以上的系統(tǒng)(JELLY_BEAN_MR1炫欺,API Level 17)
-
Environment#isExternalStorageEmulated()
返回true
除了自動清理機(jī)制乎完,系統(tǒng)設(shè)置中也給用戶提供了清理緩存數(shù)據(jù)的功能。
將媒體文件加到媒體庫
在外部存儲中品洛,$filesDir 中的媒體文件树姨,不會被當(dāng)做媒體掃描出來,加到媒體庫中桥状。
需要加入媒體庫的文件帽揪,一般應(yīng)拷貝至
Environment.getExternalStoragePublicDirectory(type)
為了媒體庫能實(shí)時掃描到新加入的文件,還應(yīng)該將文件插入到系統(tǒng)圖庫辅斟,并通知圖庫更新转晰。例子中的是增加Images,媒體庫還能處理Audio和Video士飒。
// 把文件插入到系統(tǒng)圖庫
try {
MediaStore.Images.Media.insertImage(context.getContentResolver(),
file.getAbsolutePath(), fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 通知圖庫更新
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file.getPath())));