安卓開發(fā)離不開手機(jī)存儲(chǔ),然而大部分人對(duì)于安卓開發(fā)中的存儲(chǔ)概念存在誤區(qū),內(nèi)部外部SD卡傻傻分不清?
概念掃盲
以下引用來自對(duì)官方文檔的理解
安卓手機(jī)的存儲(chǔ)分為 2 部分,內(nèi)部存儲(chǔ) ( Internal ) 和外部存儲(chǔ)
( External )
呵呵呵,先別說話,然而重點(diǎn)來了(敲黑板),按照官方的說法,
內(nèi)部存儲(chǔ)是指系統(tǒng)的存儲(chǔ)空間,沒有root是訪問不到的呦親,比如sharedPreferenced或者database都是保存在這里面的。
外部存儲(chǔ),又分為 2 部分:
機(jī)器自帶的存儲(chǔ),也就是手機(jī)廠商常說的16G,32G,64G之類的存儲(chǔ)空間
SD卡,其實(shí)正確叫法應(yīng)該是TF卡,就是可插拔的那個(gè)小東西
然而現(xiàn)實(shí)中,常常有同事把手機(jī)那個(gè)32G,64G存儲(chǔ)叫做內(nèi)部存儲(chǔ)= =,寶寶好累,人家明明是 ExternalStorage !!
希望本篇能讓大家對(duì)內(nèi)外部存儲(chǔ)有一個(gè)正確鮮明的認(rèn)識(shí)
常用路徑總結(jié)
一. 內(nèi)部存儲(chǔ)
files目錄
getFilesDir()
路徑如下
/data/user/0/<包名>/files // 7.0手機(jī) 不確定是手機(jī)原因還是系統(tǒng)原因
/data/data/<包名>/files // 4.0手機(jī)
文檔云:若想操作該路徑,你需要一個(gè)輸出流:
openFileOutput()
就像這樣:
FileOutputStream output = this.openFileOutput("- -!.txt", Context.MODE_PRIVATE);
byte[] bytes = "我是剛寫入的".getBytes();
output.write(bytes);
output.close();
注: this 是 context 對(duì)象
在
/data/data/<包名>/files/- -!.txt
路徑下會(huì)看到新文件哦
如果你還想讀取的話,文檔云:你需要一個(gè)輸入流:
FileInputStream input = this.openFileInput("- -!.txt");
byte[] bytess = new byte[input.available()];
String result = "";
while (input.read(bytess, 0, bytess.length) != -1) {
Log.d("qwer", "result ===>> " + new String(bytess, "UTF-8"));
}
Log如下
D/qwer: result ===>> 我是剛寫入的
內(nèi)部緩存目錄
getCacheDir()
文檔云:
如果您想要緩存一些數(shù)據(jù),而不是永久存儲(chǔ)這些數(shù)據(jù),應(yīng)該使用 getCacheDir() 來打開一個(gè) File,它表示您的應(yīng)用應(yīng)該將臨時(shí)緩存文件保存到的內(nèi)部目錄。
當(dāng)設(shè)備的內(nèi)部存儲(chǔ)空間不足時(shí),Android 可能會(huì)刪除這些緩存文件以回收空間。 但您不應(yīng)該依賴系統(tǒng)來為您清理這些文件, 而應(yīng)該始終自行維護(hù)緩存文件,使其占用的空間保持在合理的限制范圍內(nèi)(例如 1 MB)。 當(dāng)用戶卸載您的應(yīng)用時(shí),這些文件也會(huì)被移除
路徑如下:
/data/data/<包名>/cache
特別的,還有g(shù)etDir() :
getDir("- -!.txt", Context.MODE_PRIVATE).getAbsolutePath()
路徑如下:
/data/data/<包名>/app_- -!.txt
app_是系統(tǒng)自己加上去的
小結(jié)
內(nèi)部存儲(chǔ)就是系統(tǒng)的存儲(chǔ),沒有root你是看不到的,內(nèi)部存儲(chǔ)最大特點(diǎn)就是可以用Context對(duì)象調(diào)用各個(gè)獲取路徑的方法。比如:context.fileList()
那就是
/data/data/<包名>/files
下的文件遍歷。
而deleteFile("ABC")
就是
/data/data/<包名>/files
刪除下名為 ABC 的文件
</br></br></br>
二. 外部存儲(chǔ)
操作外部存儲(chǔ)你首先需要以下權(quán)限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
當(dāng)你申請(qǐng)了write權(quán)限,那么read權(quán)限默認(rèn)也就通過啦
再判斷狀態(tài):
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState))
外部存儲(chǔ)的根目錄
Environment.getExternalStorageDirectory()
/storage/emulated/0
這個(gè)路徑根據(jù)手機(jī)廠家不同會(huì)有些許變化
外部公共目錄
直接傳入 Environment 中的常量獲取相應(yīng)的路徑,如下:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS));
/storage/emulated/0/Alarms
或者
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
/storage/emulated/0/Pictures
公有目錄下,系統(tǒng)會(huì)區(qū)分不同類別(例如鈴聲在系統(tǒng)設(shè)置中顯示為鈴聲而不是音樂)
外部私有目錄
當(dāng)用戶卸載您的應(yīng)用時(shí),此目錄及其內(nèi)容將被刪除
系統(tǒng)媒體掃描程序不會(huì)讀取這些目錄中的文件,但是其他應(yīng)用依然可以對(duì)該目錄下的文件可以讀寫
4.4以后訪問該目錄不再需要權(quán)限了
getExternalFilesDir(String type)
/storage/emulated/0/Android/data/<包名>/files/<type>
eg.
getExternalFilesDir(Environment.DIRECTORY_MUSIC)
/storage/emulated/0/Android/data/<包名>/files/Music
特別的:ContextCompat下的
ContextCompat.getExternalFilesDirs(context,type)
返回一個(gè)File[],在4.4以后第一條數(shù)據(jù)默認(rèn)外部主存儲(chǔ)目錄,第二條數(shù)據(jù)就是sd卡路徑啦,但是注意4.4之前是沒有第二條數(shù)據(jù)的哦
外部緩存目錄
該目錄下的特點(diǎn)是卸載程序后,該目錄和其下所有文件均會(huì)被刪除
getExternalCacheDir()
/storage/emulated/0/Android/data/<包名>/cache
注意,使用該目錄注意管理空間,你不能等系統(tǒng)幫你清理,而是自己清理不再需要的緩存
特別的:ContextCompat下的
ContextCompat.getExternalCacheDirs()
道理同上
內(nèi)外部路徑匯總一覽
路徑 | 方法名 | 所屬 |
---|---|---|
/data/data/<包名>/files | getFilesDir() | 內(nèi)部 |
/data/data/<包名>/cache | getCacheDir() | 內(nèi)部 |
/data/data/<包名>/app_<name> | getDir() | 內(nèi)部 |
/storage/emulated/0 | Environment.getExternalStorageDirectory() | 外部根目錄 |
/storage/emulated/0/<type> | Environment.getExternalStoragePublicDirectory(type) | 外部九大公有目錄 |
/storage/emulated/0/Android/data/<包名>/files/<type> | getExternalFilesDir(type) | 外部私有目錄 |
/storage/emulated/0/Android/data/<包名>/cache | getExternalCacheDir() | 外部緩存目錄 |
發(fā)現(xiàn)特點(diǎn)了嗎朋友,無論外部內(nèi)部,只有路徑中有包名,那么就是私有的,而且是隨著程序的卸載而被刪除的,有包名的路徑均是Context中的方法,而公有的路徑均是Environment調(diào)用的
SD卡路徑
這個(gè)貨真真是要了老命,一般的方法根部不好使,結(jié)合網(wǎng)上有的方法加上公司項(xiàng)目中的方法,總結(jié)如下:
百分百好用的獲取SD卡路徑方法:
try {
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Method getVolumeList = null;
getVolumeList = sm.getClass().getDeclaredMethod("getVolumeList");
Object[] volumeList = (Object[]) getVolumeList.invoke(sm);
for (Object volume : volumeList) {
Method getPath = volume.getClass().getDeclaredMethod("getPath");
Method isRemovable = volume.getClass().getDeclaredMethod("isRemovable");
String path = (String) getPath.invoke(volume);
boolean removable = (Boolean) isRemovable.invoke(volume);
if (removable) {
paths.add(path);
}
}
for (String path : paths) {
Log.d("qwer", "path = > " + path);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
最后集合 path 中的值就是SD卡根目錄
/storage/sdcard1
雖然無視版本百分百好用,但是如果你的手機(jī)有SD卡槽卻沒插SD卡,該方法最后 path 返回的是 null ,也就是說該方法無法判斷到底是沒插SD卡還是根本不支持SD卡
其實(shí)還有一種方法
String path = System.getenv("SECONDARY_STORAGE");
該方法只要你手機(jī)支持SD卡,無論你插沒插SD卡,均會(huì)返回SD卡路徑,但是6.0及以上該方法被移除
Environment中源碼其實(shí)就是根據(jù)這個(gè)方法獲取路徑的
安卓官方文檔大家一定要看,他就是我們開發(fā)者的權(quán)威呀,圣經(jīng)呀!!