Android文件存儲總結(jié)

存儲路徑及演化

首先看這張文件從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()不一定有效,僅在滿足以下兩個條件時才生效。

  1. 4.2及以上的系統(tǒng)(JELLY_BEAN_MR1,API Level 17)
  2. 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())));

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容