對于任何一個(gè)應用來(lái)說(shuō),無(wú)論是PC端應用還是Android應用,存儲肯定是必不可少的。對于很多做Android開(kāi)發(fā)的同學(xué)來(lái)說(shuō),可能認為文件存儲很簡(jiǎn)單,調用一些諸如getFilesDir,getExternalStorageDirectory方法行了,但是雖然說(shuō)它們會(huì )調用相應的方法來(lái)實(shí)現簡(jiǎn)單的數據存儲。但是他們未必就搞懂了他的數據到底存在了哪里,以及他的數據是否存對了地方,或者是否做好了版本兼容。下面我將從這幾個(gè)地方來(lái)解答大家常見(jiàn)的困惑:
1、Android中內部存儲,外部存儲的概念
2、不同安卓版本下getDataDirectory,getFilesDir,getCacheDir,getDir,getExternalStorageDirectory,getExternalStoragePublicDirectory,getExternalFilesDir,getExternalCacheDir,getExternalCacheDir,getRootDirectory等方法的區別和聯(lián)系
3、清除數據和清除緩存到底清除了什么數據
4、/storage/sdcard,/sdcard,/mnt/sdcard,/storage/emulated/0之間的關(guān)系
5、一張圖看懂Ram,Rom,以及擴展存儲(TF卡)的區別;內部存儲,外部存儲的區別。
內部存儲
概念:注意內部存儲不是內存。內部存儲位于系統中很特殊的一個(gè)位置,如果你想將文件存儲于內部存儲中,那么文件默認只能被你的應用訪(fǎng)問(wèn)到,且一個(gè)應用所創(chuàng )建的所有文件都在和應用包名相同的目錄下。也就是說(shuō)應用創(chuàng )建于內部存儲的文件,與這個(gè)應用是關(guān)聯(lián)起來(lái)的。當一個(gè)應用卸載之后,內部存儲中的這些文件也被刪除。從技術(shù)上來(lái)講如果你在創(chuàng )建內部存儲文件的時(shí)候將文件屬性設置成可讀,其他app能夠訪(fǎng)問(wèn)自己應用的數據,前提是他知道你這個(gè)應用的包名,如果一個(gè)文件的屬性是私有(private),那么即使知道包名其他應用也無(wú)法訪(fǎng)問(wèn)。 內部存儲空間十分有限,因而顯得可貴,另外,它也是系統本身和系統應用程序主要的數據存儲所在地,一旦內部存儲空間耗盡,手機也就無(wú)法使用了。所以對于內部存儲空間,我們要盡量避免使用。Shared Preferences和SQLite數據庫都是存儲在內部存儲空間上的。內部存儲一般用Context來(lái)獲取和操作。
訪(fǎng)問(wèn)內部存儲的API方法:
1、Environment.getDataDirectory()
2、getFilesDir().getAbsolutePath()
3、getCacheDir().getAbsolutePath()
4、getDir(“myFile”, MODE_PRIVATE).getAbsolutePath()
外部存儲
概念:最容易混淆的是外部存儲,因為老的Android系統的跟新的Android系統是有差別的,很多人去網(wǎng)上查找資料,看了一下以前的資料,又看了一下現在的資料,但是發(fā)現它們說(shuō)法不一樣然后就困惑了。首先說(shuō)一個(gè)大家普遍的概念“如果在pc機上是區分外部存儲和內部存儲的話(huà),那么電腦自帶的硬盤(pán)算是內部存儲,U盤(pán)或者移動(dòng)硬盤(pán)就是外部存儲了?!币虼撕芏嗳藥е?zhù)這樣的理解去看待安卓手機,把內置存儲(機身存儲)當做內部存儲,而把擴展的SD卡當做是外部存儲。這么認為確實(shí)沒(méi)錯,因為在4.4(API19)以前的手機上確實(shí)是這樣的,手機自身帶的存儲卡就是內部存儲,而擴展的SD卡就是外部存儲。但是從4.4的系統開(kāi)始,很多的中高端機器都將自己的機身存儲擴展到了8G以上,比如有的人的手機是16G的,有的人的手機是32G的,但是這個(gè)16G,32G是內部存儲嗎,不是的?。?!,它們依然是外部存儲,也就是說(shuō)4.4系統及以上的手機將機身存儲存儲(手機自身帶的存儲叫做機身存儲)在概念上分成了”內部存儲internal” 和”外部存儲external” 兩部分。既然16G,32G是外部存儲,那有人又有疑惑了,那4.4系統及以上的手機要是插了SD卡呢,SD卡又是什么呢,如果SD卡也是外部存儲的話(huà),那怎么區分機身存儲的外部存儲跟SD卡的外部存儲呢?對,SD卡也是外部存儲,那怎么區分呢,在4.4以后的系統中,API提供了這樣一個(gè)方法來(lái)遍歷手機的外部存儲路徑:
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卡的話(huà),那么它打印的路徑就有兩條了,例如我的華為榮耀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卡的路徑
他們統稱(chēng)為外部存儲
訪(fǎng)問(wèn)外部存儲的API方法:
1、Environment.getExternalStorageDirectory().getAbsolutePath()
2、Environment.getExternalStoragePublicDirectory(“”).getAbsolutePath()
3、getExternalFilesDir(“”).getAbsolutePath()
4、getExternalCacheDir().getAbsolutePath()
大家對Android的外部存儲會(huì )產(chǎn)生疑問(wèn),主要是現在很多的手機已經(jīng)從物理上看不到外部存儲了,以前的手機都有,就是那種黑色的內存卡,8G,16G,32G的,可以像U盤(pán)一樣插拔,以前很流行,存儲空間不夠了,就去買(mǎi)個(gè)內存卡(準確說(shuō)是SD卡,說(shuō)成內存卡又會(huì )引起誤解)回來(lái),后來(lái)的手機比如現在我用的華為榮耀7,廠(chǎng)家已經(jīng)把機身存儲擴展到了16G了,只是在存儲概念上了分為了內部存儲(內部internal)和外部存儲(外部external),其實(shí)它們都集成在一起了。當然如果你覺(jué)得16G不夠用,那他支持通過(guò)插SD卡來(lái)擴充容量嗎?支持的,榮耀7為例,它是三合二卡槽??ú?:Nano SIM卡;卡槽2:Nano SIM卡或Micro SD卡。默認卡槽1為4G主卡,可以在設置中更改4G主卡卡槽;不支持熱插拔,插拔卡托后需重啟手機。這樣插入的SD卡也屬于外部存儲。所以手機的外部存儲可能包含兩部分,一是機身存儲的外部存儲部分,還有一個(gè)是SD卡部分
上面這些方法,我們可能似曾相識,但是對于有些同學(xué)來(lái)說(shuō)卻又很難分清出,主要還是不同的Android版本的問(wèn)題。為了方便大家理解,我先簡(jiǎn)要介紹以上各個(gè)方法,為方便大家理解我把這些方法的結果打印出來(lái)(以下的打印結果是基于榮耀7的(系統版本6.0):
1、Environment.getDataDirectory() = /data
這個(gè)方法是獲取內部存儲的根路徑
2、getFilesDir().getAbsolutePath() = /data/user/0/packname/files
這個(gè)方法是獲取某個(gè)應用在內部存儲中的files路徑
3、getCacheDir().getAbsolutePath() = /data/user/0/packname/cache
這個(gè)方法是獲取某個(gè)應用在內部存儲中的cache路徑
4、getDir(“myFile”, MODE_PRIVATE).getAbsolutePath() = /data/user/0/packname/app_myFile
這個(gè)方法是獲取某個(gè)應用在內部存儲中的自定義路徑
方法2,3,4的路徑中都帶有包名,說(shuō)明他們是屬于某個(gè)應用
…………………………………………………………………………………………
5、Environment.getExternalStorageDirectory().getAbsolutePath() = /storage/emulated/0
這個(gè)方法是獲取外部存儲的根路徑
6、Environment.getExternalStoragePublicDirectory(“”).getAbsolutePath() = /storage/emulated/0
這個(gè)方法是獲取外部存儲的根路徑
7、getExternalFilesDir(“”).getAbsolutePath() = /storage/emulated/0/Android/data/packname/files
這個(gè)方法是獲取某個(gè)應用在外部存儲中的files路徑
8、getExternalCacheDir().getAbsolutePath() = /storage/emulated/0/Android/data/packname/cache
這個(gè)方法是獲取某個(gè)應用在外部存儲中的cache路徑
注意:其中方法7和方法8如果在4.4以前的系統中g(shù)etExternalFilesDir(“”)和getExternalCacheDir()將返回null,如果是4.4及以上的系統才會(huì )返回上面的結果,也即4.4以前的系統沒(méi)插SD卡的話(huà),就沒(méi)有外部存儲,它的SD卡就等于外部存儲;而4.4及以后的系統外部存儲包括兩部分,getExternalFilesDir(“”)和getExternalCacheDir()獲取的是機身存儲的外部存儲部分,也即4.4及以后的系統你不插SD卡,它也有外部存儲,既然getExternalFilesDir(“”)和getExternalCacheDir()獲取的是機身存儲的外部存儲部分,那么怎么獲取SD卡的存儲路徑呢,還是通過(guò)上面提到的getExternalFilesDirs(Environment.MEDIA_MOUNTED)方法來(lái)獲取了,不知道Android有沒(méi)有提供相關(guān)的API接口來(lái)獲取SD卡的存儲路徑,大家可以去查資料。又重復了上面的話(huà),主要是提醒大家要注意不同的Android版本是有差別的,這個(gè)最坑了。
…………………………………………………………………………………………
Environment.getDownloadCacheDirectory() = /cache
Environment.getRootDirectory() = /system
這兩個(gè)方法沒(méi)什么說(shuō)的了,每個(gè)版本的android系統都一樣
…………………………………………………………………………………………
從上面我們很清楚的可以看到上面的方法可以分為三類(lèi),我用橫線(xiàn)隔開(kāi)了。第一類(lèi)是位于根目錄/data下;還有一類(lèi)是位于根目錄/storage下,可以看到調用它們的API方法都帶了一個(gè)External;另外一類(lèi)不在/data下也不再/storage下,比如系統文件/system,或者緩存文件/cache。
/data目錄下的文件物理上存放在我們通常所說(shuō)的內部存儲里面
/storage目錄下的文件物理上存放在我們通常所說(shuō)的外部存儲里面
/system用于存放系統文件,/cache用于存放一些緩存文件,物理上它們也是存放在內部存儲里面的
下面來(lái)看一下大家常見(jiàn)的疑問(wèn)
疑問(wèn)1、那getFilesDir().getAbsolutePath()和getCacheDir().getAbsolutePath()有什么區別呢?
其實(shí)是沒(méi)有什么區別的,我們可以看下面一張圖:
public static String getFilePath(Context context,String dir) { String directoryPath=""; if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ) {//判斷外部存儲是否可用 directoryPath =context.getExternalFilesDir(dir).getAbsolutePath(); }else{//沒(méi)外部存儲就使用內部存儲 directoryPath=context.getFilesDir()+File.separator+dir; } File file = new File(directoryPath); if(!file.exists()){//判斷文件目錄是否存在 file.mkdirs(); } return directoryPath;}為了讓大家更好地理解不同版本的Android系統的存儲差異我做了下列一份說(shuō)明表:
比較的是我向getFilesDir().getAbsolutePath()路徑下和getExternalFilesDir(“”).getAbsolutePath()路徑各寫(xiě)入19.48M數據前后的差別
表一、4.1.1系統,帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/data/packname/files | getFilesDir() | 1.59GB | 1.57GB | 內部存儲 |
| /storage/sdcard0/Android /data/packname/files | getExternal StorageDirectory() | 1.47GB | 1.45GB | 外部存儲(SD卡) |
表二、4.1.1系統,不帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/data/packname/files | getFilesDir() | 1.59GB | 1.47GB | 內部存儲 |
| 路徑不存在 | getExternal StorageDirectory() | ~~~ | ~~~ | 沒(méi)插SD卡 |
表三、4.2.1系統,帶有SD卡 (模擬器)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/data/packname/files | getFilesDir() | 1.85GB | 1.83GB | 內部存儲 |
| /mnt/sdcard/Android /data/packname/files | getExternal StorageDirectory() | 98.42MB | 78.93MB | 外部存儲(SD卡) |
表四、4.4.2系統,帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/data/packname/files | getFilesDir() | 2.22GB | 2.18GB | 內部存儲 |
| /storage/emulated/0/Android /data/packname/files | getExternal StorageDirectory() | 2.20GB | 2.16GB | 機身外部存儲 |
| /storage/sdcard1 | getExternalFilesDirs | 1.47GB | 1.47GB | 外部存儲(SD卡 沒(méi)有向其寫(xiě)數據,只是讀?。?/td> |
表五、4.4.2系統,不帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/data/packname/files | getFilesDir() | 2.22GB | 2.18GB | 內部存儲 |
| /storage/emulated/0/Android /data/packname/files | getExternal StorageDirectory() | 2.20GB | 2.16GB | 機身外部存儲 |
表六、6.0.0系統,帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | 容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/user/0/packname/files | getFilesDir() | 11.94GB | 11.90GB | 內部存儲 |
| /storage/emulated/0/Android /data/packname/files | getExternal StorageDirectory() | 11.92GB | 11.88GB | 機身外部存儲 |
| /storage/B3E4-1711 | getExternalFilesDirs | 1.47GB | 1.47GB | 外部存儲(SD卡) 沒(méi)有向其寫(xiě)數據,只是讀取 |
表七、6.0.0系統,不帶有SD卡 (真機)
| 存儲位置 | 獲取路徑的方法 | 容量(寫(xiě)入前) | /storage/容量(寫(xiě)入后) | 備注 |
|---|---|---|---|---|
| /data/user/0/packname/files | getFilesDir() | 11.93GB | 11.89GB | 內部存儲 |
| /storage/emulated/0/Android /data/packname/files | getExternal StorageDirectory() | 11.91GB | 11.87GB | 機身外部存儲 |
注:上述容量指的是該路徑所在根路徑的可用容量,比如/data/data/packname/files的容量是指/data的可用容量,/storage/sdcard0/Android/data/packname/files指的是/storage/sdcard0的可用容量,而一般在4.4及以上的系統中,我們很少操作SD
這個(gè)很容易搞混,為什么呢?通過(guò)上面我們知道:
/data/user/0/packname/files它是用來(lái)存儲普通數據的
/data/user/0/packname/cache它是用來(lái)存儲緩存數據的
所以很多人就以為我清除數據時(shí)清除的肯定就是files下的數據,而我清除緩存數據時(shí)清除的肯定就是cache下的數據,但是事實(shí)卻不是這樣的。正確應該是:
清除緩存:我們知道應用程序在運行過(guò)程中需要經(jīng)過(guò)很多過(guò)程,比如讀入程序,計算,輸入輸出等等,這些過(guò)程中肯定會(huì )產(chǎn)生很多的數據,它們在內存中,以供程序運行時(shí)調用。所以清除緩存清除的是APP運行過(guò)程中所產(chǎn)生的臨時(shí)數據。
清除數據:清除數據才是真正的刪除了我們保存在文件中的數據(永久性數據,如果不人為刪除的話(huà)會(huì )一直保存在文件中)例如當我們在設置里面清除了某個(gè)應用的數據,那么/data/user/0/packname/和/storage/emulated/0/Android/data/packname/下的文件里面的數據會(huì )全部刪除,包括cache,files,lib,shared_prefs等等。
從上面的表中我們可以發(fā)現,在4.1系統中,getExternalStorageDirectory方法獲取到的路徑為/storage/sdcard0;4.2系統中g(shù)etExternalStorageDirectory方法獲取到的路徑為/mnt/sdcard,因為4.2是模擬器打印的結果,如果是真機的話(huà)也是/storage/sdcard0;4.4的getExternalStorageDirectory方法獲取到的路徑為/storage/emulated/0,它的SD卡存儲路徑為/storage/sdcard1;6.0的getExternalStorageDirectory方法獲取到的路徑為/storage/emulated/0,它的SD卡存儲路徑為/storage/B3E4-1711;另外根據測試在4.0上getExternalStorageDirectory方法獲取到的路徑為/mnt/sdcard。所以在真機上,getExternalStorageDirectory獲取到的路徑如下表所示:
| 系統版本 | 結果 |
|---|---|
| 4.0 | /mnt/sdcard |
| 4.1 | /storage/sdcard0 |
| 4.2 | /storage/sdcard0 |
| 4.4 | /storage/emulated/0 |
| 6.0 | /storage/emulated/0 |
要理解/storage/sdcard,/sdcard,/mnt/sdcard,/storage/emulated/0之間的關(guān)系,我們需要先要了解一下linux文件掛載的概念,關(guān)于掛載大家可以自行去百度。還有我們不明白為什么會(huì )有有/storage/sdcard,/sdcard,/mnt/sdcard,/storage/emulated/0這么多目錄,讓人看起來(lái)眼花繚亂,要詳細了解請仔細看下面的文章,下面的文章是我摘自關(guān)于android的4.2的0文件夾的詳解
—- android 4.0 —-
在galaxy nexus(GN)手機上userdata分區很大,被掛在/data目錄,用戶(hù)的數據通常是放在sd卡上,然而gn是沒(méi)有sd卡的,所以google想了一個(gè)辦法,就是虛擬一個(gè)。
所以,在userdata分區下有個(gè)目錄叫media,是內置sd卡的數據存儲位置,使用fuse技術(shù)將/data/media虛擬成為一個(gè)叫做/dev/fuse的設備,為了讓程序能認出來(lái),被同時(shí)掛載在 /mnt/sdcard 目錄,又為了兼容以前的程序,做了一個(gè)快捷方式(linux系統里叫軟連接) /sdcard 指向的是 /mnt/sdcard .
當然,這些都是4.0的做法。
—- android 4.1 —-
在4.1里,同樣也會(huì )使用fuse技術(shù),/dev/fuse 會(huì )被同時(shí)掛載到/storage/sdcard0 目錄,這個(gè)sdcard0表示第一個(gè)sd卡(如果有外置sd卡,那會(huì )多一個(gè) /storage/sdcard1,比如我的xoom), /sdcard 軟連接會(huì )指向 /storage/sdcard0 ,此時(shí)/mnt/sdcard 也是個(gè)軟連接,會(huì )指向/storage/sdcard0。
如果你通過(guò)otg線(xiàn)接U盤(pán),會(huì )被掛載到 /storage/usb0目錄,stickmount這個(gè)軟件為了讓圖庫、快圖、mx player等軟件,能看到u盤(pán)里的數據,又同時(shí)掛載到 /storage/sdcard0/usStorage/sda1.
也許你會(huì )問(wèn),為什么不是usb0,而是sda1,這是linux的對硬盤(pán)的命名方式,如果你的u盤(pán)有多個(gè)分區,就分別是sda1,sda2這樣一直排下去了。
—- android 4.2 —-
好了,我們開(kāi)始說(shuō)4.2系統。
谷歌是不是沒(méi)事干啊,非要給android搞個(gè)多用戶(hù),你想想啊,在中國,可能因為經(jīng)濟問(wèn)題,家里不是每人一個(gè)電腦,在美國,幾乎需要用電腦的人,都會(huì )自己有一臺或多臺,一臺電腦多人用的情況少之又少,這就是為什么叫PC了,顧名思義,個(gè)人電腦。像手機和平板這些東西,更加私人化了,很少公用了吧,我想在中國也是如此吧。
當然,谷歌也不完全是抽風(fēng),因為他有更大的戰略部署,而且平板也的確有多人用的可能。
所以谷歌搞出來(lái)一個(gè)多用戶(hù),那每個(gè)人的應用、數據、個(gè)性配置都要分開(kāi)吧。 應用和個(gè)性配置好弄,想想啊,通過(guò)權限控制,每人只能看自己的應用就行了,桌面也可以用自己的。
那數據怎么辦????
好吧,調整用戶(hù)數據的掛載結構。android 4.2,同樣也會(huì )使用fuse技術(shù)/dev/fuse 會(huì )被掛載到/storage/emulated/0 目錄,為什么是0呢,你還記得上邊的sdcard0吧,第一個(gè)的意思。(如果有第二個(gè),應該就是/storage/emulated/1,我們的三兒子沒(méi)有外置sd卡,所以沒(méi)法驗證)
為了兼容以前,同時(shí)掛載到 /storage/emulated/legacy (故名思議,傳統的),還建立三個(gè)軟連接 /storage/sdcard0 ,/sdcard,/mnt/sdcard ,都指向 /storage/emulated/legacy
很多同學(xué)可能不會(huì )認真看上面,這里我就簡(jiǎn)單總結一下:
1、其中sdcard/、mnt/sdcard、storage/sdcard0、storage/emulated/0、storage/emulated/legacy都是同一個(gè)路徑的不同”指針“,指向的是同一個(gè)地方,只是不同Android版本的叫法不一樣。
2、如果大家想了解每個(gè)版本的外部存儲路徑,同學(xué)們可以通過(guò)獲取getExternalStorageDirectory方法的打印結果進(jìn)行對比
聯(lián)系客服