因為專案需要,最近學了很多資料本地持久化的知識。有很多情景我們都需要遇到檔案儲存:從儲存使用者的登入狀態到記錄瀏覽資訊,從儲存圖片到下載大型檔案。所以有必要了解下Android的檔案儲存系統,從而輕鬆地去管理我們應用平常產生的資料。
內部儲存
這是按儲存的位置來分的。應用對內部儲存操作不需要任何的許可權,這就意味著不需要在AndroidManifest.xml
裡宣告許可權,也不需要在Android6.0上進行許可權適配。其實做過許可權適配同學都清楚,只有外部儲存的許可權:Manifest.permission.READ_EXTERNAL_STORAGE
和Manifest.permission.WRITE_EXTERNAL_STORAGE
。個人理解:許可權是為了系統保證應用安全和使用者安全而設的,既然系統對內部儲存有著最高的管理許可權,應用只能在系統分配的空間儲存檔案,就沒必要有這個內部儲存許可權
了。
我們App在內部儲存都有自己的專有目錄: /data/data/PackageName
。存放了 WebView 快取頁面資訊,SharedPreferences 和 資料庫資料等。不過沒root的是看不到的.當然我們可以通過Android Studio主介面右下處的Device File Exploer
來檢視:
預建工作目錄
建立一個新應用,包名:com.renny.storage
,系統在內部儲存預設建立了三個空資料夾,通過Context來獲取下:
File getFilesDir()
:返回內部儲存的Files資料夾File getCacheDir()
:返回內部儲存的cache資料夾File getCodeCacheDir()
:返回內部儲存的code_cache資料夾,要求Android5.0+File getDataDir()
: 返回內部儲存的根資料夾,要求Android7.0+
通常我們將檔案存放在的Files資料夾,通過fileList()
可以獲取該資料夾下的全部檔案的檔名。openFileInput()
和 openFileOutput()
方法也是在該目錄下操作檔案。這些方法都是Context裡的,在activity中可以直接呼叫。舉個例子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try{
FileOutputStream outputStream = openFileOutput("hello.text", Context.MODE_PRIVATE);
outputStream.write( "Hello world!".getBytes());
outputStream.close();
} catch(IOException e) {
e.printStackTrace();
}
String[] files = fileList();
for (String file : files) {
Log.d("file--",file);
}
}
複製程式碼
這樣就會在Files資料夾下面生成一個hello.text
檔案了,列印出的資訊就是 hello.text
。
其他工作目錄
上面僅僅是一個全新應用預先建立的內部儲存目錄。其他相關的子目錄如下(使用時建立):
-
/data/data/PackageName/shared_prefs/
:
用來儲存 SharedPreference,相關函式為:getSharedPreferences(String fileName, int mode);
-
/data/data/PackageName/databases/
用來儲存資料庫檔案,相關函式:
getDatabasePath()
; -
/data/data/PackageName/app_webview
-
/data/data/PackageName/xxxwebviewcachexxx
儲存應用內建 webview 所產生的 cache 和 cookies 等,該目錄由於 android 版本不同名字和位置也可能不同; -
/data/data/PackageName/lib
用來儲存該應用的 .so 靜態庫檔案;
小結
內部儲存空間較小,因為只對應用本身可見,適合儲存一些私密資訊,比如登入資訊,需要加密的資料等等,當一個應用解除安裝之後,內部儲存中的這些檔案也會被刪除。另外內部儲存的檔案讀取速度一般也是高於外部儲存的。
外部儲存
外部儲存空間就非常大了,大家買的手機32Gb,64Gb容量,還有還有XXGb的SD卡,都是外部儲存。外部儲存可以直接在手機的檔案管理應用中瀏覽。外部儲存中的檔案是可以被使用者或者其他應用程式修改的(系統不管了),所以讀寫也需要Manifest.permission.READ_EXTERNAL_STORAGE
和Manifest.permission.WRITE_EXTERNAL_STORAGE
許可權,來讓使用者自己管理。
有了許可權,外部儲存的所有目錄我們都能訪問到。
比如根目錄:
Environment.getExternalStorageDirectory()
下面我們按照儲存內容的用途來介紹:
公共檔案目錄
Environment.getExternalStoragePublicDirectory(int type)
根據type會返回對應的檔案目錄。這些檔案目錄就是用來存放所有App都可能會用到的一些檔案。type有很多種,比如:
Environment.DIRECTORY_DCIM
,得到的就是外部儲存根資料夾下的DCIM資料夾,也就是手機儲存照片的地方。常用的還有:
Environment.DIRECTORY_DOWNLOADS
Download資料夾Environment.DIRECTORY_MUSIC
Music資料夾Environment.DIRECTORY_MOVIES
Movies資料夾
等等等等。一般把需要和其他應用共享的檔案都可以放這裡(不過情況好像不是很多)。
應用專屬檔案目錄
雖然在外部儲存管不了應用在哪存取檔案,但也不能任由應用胡亂在任何位置存檔案,而且這些檔案在應用解除安裝了以後也要刪除吧,不然到後來外部儲存全是解除安裝後應用留下的垃圾檔案。所以Android有個目錄用來專門給應用存檔案(當然想不想在那存,系統管不了)。目錄結構類似於內部儲存:
/storage/emulated/0/Android/data/PackageName/
方法也和內部儲存很類似:
-
Context.getExternalFilesDir(String type)
:Android/data/PackageName/files資料夾 -
Context.getExternalCacheDir()
:Android/data/PackageName/cache資料夾 上面的type和Environment.getExternalStoragePublicDirectory(int type)
用法是類似的,有一點不同:可以為空:
getContext().getExternalFilesDir(null)
:此時就是files根資料夾Android/data/packageName/files
;也可以傳一個值:
getContext().getExternalFilesDir("apple")
:此時就是files資料夾下的子資料夾:Android/data/packageName/files/apple
。
一些總結
- 一般情況下有包名的路徑我們都是呼叫Context中的方法來獲得,沒有包名的路徑,我們直接呼叫Environment中的方法獲得。
- 使用外部儲存前要檢測許可權和檢測狀態是否可用(通常對於sd卡來說),如果不能用就只能用內部儲存了:
state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
//todo
}
複製程式碼
- 關於設定->應用->應用詳情裡面的”清除資料“與”清除快取“選項,前者會刪除所有的內部儲存和外部儲存的應用專屬檔案目錄的資料夾;後者只會清楚內部儲存和外部儲存專屬檔案目錄下的快取資料夾。