Android 檔案儲存淺析

huansky發表於2020-07-26

最近做的一個需求和檔案儲存有關係。由於之前沒有系統梳理過,對檔案儲存方面的知識一直很懵懂。趁著週末有時間,趕緊梳理一波。

這首從網上找到的一張圖,很好的概括了外部儲存和內部儲存。

 

下面我們再來具體介紹相關知識和內容。

內部儲存 

內部儲存,位於data/data/包名/路徑下

是否需要使用者許可權:否

是否能被其他應用訪問:否

解除安裝應用資料是否被刪除:是

內部儲存控制元件不需要使用者許可權,這意味著我們不需要使用者去授權下面的許可權:

android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE

對於裝置中每一個安裝的 App,系統都會在 data/data 目錄下以應用程式包名自動建立與之對應的資料夾,可以直接讀寫該目錄下的檔案。而且該目錄下的檔案不能被其他應用訪問。這也就保證了我們應用內部儲存的檔案的安全性和隱私性,如果我們需要檢視自己應用內部的檔案,我們可以通過 Android Studio的Device File Explore 工具進行訪問:

   

 

通過這個,可以檢視對應應用的儲存檔案。

/data/data/應用名/cache :存放的是APP的快取資訊

/data/data/應用名/code_cache :在執行時存放應用產生的編譯或者優化的程式碼

/data/data/應用名/files : 存放APP的檔案資訊

還有一些執行時,產生的資料夾,例如呼叫 SharedPreference 所產生的 /data/data/應用包名/shared_prefs 目錄,存放著 app 的 SharedPreference 所產生的 xml 檔案,還有呼叫資料庫所產生的 **/data/data/應用包名/databases/** 資料夾,這裡就不一一舉例。

從技術上來講如果你在建立內部儲存檔案的時候將檔案屬性設定成可讀,其他 app 能夠訪問自己應用的資料,前提是他知道你這個應用的包名,如果一個檔案的屬性是私有(private),那麼即使知道包名其他應用也無法訪問。 內部儲存空間十分有限,因而顯得可貴,另外,它也是系統本身和系統應用程式主要的資料儲存所在地,一旦內部儲存空間耗盡,手機也就無法使用了。所以對於內部儲存空間,我們要儘量避免使用。Shared Preferences 和 SQLite 資料庫都是儲存在內部儲存空間上的。內部儲存一般用 Context 來獲取和操作。
訪問內部儲存的API方法:

  1. getFilesDir().getAbsolutePath()  : /data/user/0/com.example.myapplication/files

  2. getCacheDir().getAbsolutePath() :  /storage/emulated/0/Android/data/com.example.myapplication/cache

  3. getDir(“myFile”, MODE_PRIVATE).getAbsolutePath()  : /data/user/0/com.example.myapplication/app_myfile

  4. getCodeCacheDir().getAbsolutePath() : /data/user/0/com.example.myapplication/code_cache  ,要求Android5.0+

外部儲存

概念:最容易混淆的是外部儲存,因為老的 Android 系統的跟新的 Android 系統是有差別的,很多人去網上查詢資料,看了一下以前的資料,又看了一下現在的資料,但是發現它們說法不一樣然後就困惑了。

首先說一個大家普遍的概念 "如果在 pc 機上是區分外部儲存和內部儲存的話,那麼電腦自帶的硬碟算是內部儲存,U盤或者行動硬碟就是外部儲存了。" 因此很多人帶著這樣的理解去看待安卓手機,把內建儲存(機身儲存)當做內部儲存,而把擴充套件的 SD 卡當做是外部儲存。

這麼認為確實沒錯,因為在 4.4(API19)以前的手機上確實是這樣的,手機自身帶的儲存卡就是內部儲存,而擴充套件的SD卡就是外部儲存。

但是從 4.4 的系統開始,很多的中高階機器都將自己的機身儲存擴充套件到了 8G 以上,比如有的人的手機是 16G 的,有的人的手機是 32G 的,但是這個 16G,32G 是內部儲存嗎,不是的,它們依然是外部儲存。

也就是說 4.4 系統及以上的手機將機身儲存儲存(手機自身帶的儲存叫做機身儲存)在概念上分成了 "內部儲存internal" 和 "外部儲存external" 兩部分。既然 16G,32G 是外部儲存,那有人又有疑惑了,那 4.4 系統及以上的手機要是插了 SD 卡呢,SD 卡又是什麼呢,如果 SD 卡也是外部儲存的話,那怎麼區分機身儲存的外部儲存跟 SD 卡的外部儲存呢?

對,SD卡也是外部儲存,那怎麼區分呢,在4.4以後的系統中,API提供了這樣一個方法來遍歷手機的外部儲存路徑:

File[] files;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
    for(File file:files){
        Log.e("main",file);
    }
}

如果你的手機插了SD卡的話,那麼它列印的路徑就有兩條了,例如我的華為榮耀 7 插了SD卡,它的結果如下:

/storage/emulated/0/Android/data/packname/files/mounted
/storage/B3E4-1711/Android/data/packname/files/mounted

其中 /storage/emulated/0 目錄就是機身儲存的外部儲存路徑,而 /storage/B3E4-1711/ 就是 SD 卡的路徑,他們統稱為外部儲存。

一般對於外部儲存可以分為兩類,外部公有和外部私有。

外部公有

是否需要使用者許可權:

是否能被其他應用訪問:

解除安裝應用資料是否被刪除:

公共目錄必須需要使用者授權讀寫的許可權,這意味著我們需要在 AndroidManifest.xml 中註冊使用者許可權。

    <!-- 外部儲存寫入資料許可權 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

並且在 Android 6.0 系統之後需要申請使用者許可權,並獲得使用者授權,才能讀寫檔案。

公共目錄相對開放,我們可以訪問其他APP存在公共目錄下的檔案,並且當 APP 被刪除時,並不會刪除應用存在公共目錄下的檔案。

我們可以通過 Environment 物件,訪問讀寫公共目錄的檔案。

在對外部儲存進行讀寫的時候,應該先判斷一下外部儲存的狀態,是否能夠支援讀寫。

Environment.getExternalStorageState() 
/**       {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
 *        {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
 *        {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
 *        {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
 *        {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}
 */

只有在返回值為 MEDIA_MOUNTED 表示當前是可正常讀寫的。

接下來讓我們看看相關的API。

1. Environment.getExternalStorageDirectory() : /storage/emulated/0

2. Environment.getExternalStoragePublicDirectory(String type)  
       Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getAbsolutePath()  : /storage/emulated/0/Documents
    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath() : /storage/emulated/0/Music

外部私有

是否需要使用者許可權:4.4以上不需要

是否能被其他應用訪問:否

解除安裝應用資料是否被刪除:是

私有目錄,在 Android4.4 系統以上。不需要註冊和使用者授權外部私有儲存的讀寫的許可權,就可以在應用的外部私有進行讀寫檔案。並且檔案不能被其他應用所訪問,具有較好的隱私性和安全性,並且在使用者刪除的時候,對應的應用私有目錄也會被刪除。

私有目錄地址:/storage/emulated/0/Android/data/應用包名

相關API如下:

getExternalCacheDir().getAbsolutePath()  // /storage/emulated/0/Android/data/com.example.myapplication/cache

getExternalFilesDir("mytest").getAbsolutePath() // /storage/emulated/0/Android/data/com.example.myapplication/files/mytest
getExternalFilesDir(null).getAbsolutePath()  // /storage/emulated/0/Android/data/com.example.myapplication/files

總結

本文詳細介紹了 android 的外部儲存和私有儲存。大家在有儲存檔案的需求的時候,根據自己的需要,選擇到底是存在哪裡比較合適。內部儲存相對較小,不介意把一些大檔案存在其中。應該存在外部儲存會更好。對於可以給其他檔案訪問的,可以存在外部儲存的公有檔案裡面。

 

參考文獻

相關文章