安卓開發(fā)離不開手機存儲盅弛,然而大部分人對于安卓開發(fā)中的存儲概念存在誤區(qū),內部外部SD卡傻傻分不清叔锐?
概念掃盲
以下引用來自對官方文檔的理解
安卓手機的存儲分為 2 部分挪鹏,內部存儲 ( Internal ) 和外部存儲
( External )
呵呵呵,先別說話愉烙,然而重點來了(敲黑板)讨盒,按照官方的說法,
內部存儲是指系統的存儲空間步责,沒有root是訪問不到的呦親返顺,比如sharedPreferenced或者database都是保存在這里面的禀苦。
外部存儲,又分為 2 部分:
機器自帶的存儲遂鹊,也就是手機廠商常說的16G振乏,32G,64G之類的存儲空間
SD卡秉扑,其實正確叫法應該是TF卡慧邮,就是可插拔的那個小東西
然而現實中,常常有同事把手機那個32G舟陆,64G存儲叫做內部存儲= =误澳,寶寶好累,人家明明是 ExternalStorage G厍忆谓!
希望本篇能讓大家對內外部存儲有一個正確鮮明的認識
常用路徑總結
一. 內部存儲
files目錄
getFilesDir()
路徑如下
/data/user/0/<包名>/files // 7.0手機 不確定是手機原因還是系統原因
/data/data/<包名>/files // 4.0手機
文檔云:若想操作該路徑,你需要一個輸出流:
openFileOutput()
就像這樣:
FileOutputStream output = this.openFileOutput("- -!.txt", Context.MODE_PRIVATE);
byte[] bytes = "我是剛寫入的".getBytes();
output.write(bytes);
output.close();
注: this 是 context 對象
在
/data/data/<包名>/files/- -!.txt
路徑下會看到新文件哦
如果你還想讀取的話踱承,文檔云:你需要一個輸入流:
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 ===>> 我是剛寫入的
內部緩存目錄
getCacheDir()
文檔云:
如果您想要緩存一些數據倡缠,而不是永久存儲這些數據,應該使用 getCacheDir() 來打開一個 File勾扭,它表示您的應用應該將臨時緩存文件保存到的內部目錄毡琉。
當設備的內部存儲空間不足時,Android 可能會刪除這些緩存文件以回收空間妙色。 但您不應該依賴系統來為您清理這些文件桅滋, 而應該始終自行維護緩存文件,使其占用的空間保持在合理的限制范圍內(例如 1 MB)身辨。 當用戶卸載您的應用時丐谋,這些文件也會被移除
路徑如下:
/data/data/<包名>/cache
特別的,還有getDir() :
getDir("- -!.txt", Context.MODE_PRIVATE).getAbsolutePath()
路徑如下:
/data/data/<包名>/app_- -!.txt
app_是系統自己加上去的
小結
內部存儲就是系統的存儲,沒有root你是看不到的煌珊,內部存儲最大特點就是可以用Context對象調用各個獲取路徑的方法号俐。比如:context.fileList()
那就是
/data/data/<包名>/files
下的文件遍歷。
而deleteFile("ABC")
就是
/data/data/<包名>/files
刪除下名為 ABC 的文件
</br></br></br>
二. 外部存儲
操作外部存儲你首先需要以下權限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
當你申請了write權限定庵,那么read權限默認也就通過啦
再判斷狀態(tài):
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState))
外部存儲的根目錄
Environment.getExternalStorageDirectory()
/storage/emulated/0
這個路徑根據手機廠家不同會有些許變化
外部公共目錄
直接傳入 Environment 中的常量獲取相應的路徑吏饿,如下:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS));
/storage/emulated/0/Alarms
或者
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
/storage/emulated/0/Pictures
公有目錄下,系統會區(qū)分不同類別(例如鈴聲在系統設置中顯示為鈴聲而不是音樂)
外部私有目錄
當用戶卸載您的應用時蔬浙,此目錄及其內容將被刪除
系統媒體掃描程序不會讀取這些目錄中的文件,但是其他應用依然可以對該目錄下的文件可以讀寫
4.4以后訪問該目錄不再需要權限了
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)
返回一個File[]猪落,在4.4以后第一條數據默認外部主存儲目錄,第二條數據就是sd卡路徑啦畴博,但是注意4.4之前是沒有第二條數據的哦
外部緩存目錄
該目錄下的特點是卸載程序后笨忌,該目錄和其下所有文件均會被刪除
getExternalCacheDir()
/storage/emulated/0/Android/data/<包名>/cache
注意,使用該目錄注意管理空間俱病,你不能等系統幫你清理官疲,而是自己清理不再需要的緩存
特別的:ContextCompat下的
ContextCompat.getExternalCacheDirs()
道理同上
內外部路徑匯總一覽
路徑 | 方法名 | 所屬 |
---|---|---|
/data/data/<包名>/files | getFilesDir() | 內部 |
/data/data/<包名>/cache | getCacheDir() | 內部 |
/data/data/<包名>/app_<name> | getDir() | 內部 |
/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ā)現特點了嗎朋友袱结,無論外部內部,只有路徑中有包名途凫,那么就是私有的垢夹,而且是隨著程序的卸載而被刪除的,有包名的路徑均是Context中的方法颖榜,而公有的路徑均是Environment調用的
SD卡路徑
這個貨真真是要了老命棚饵,一般的方法根部不好使,結合網上有的方法加上公司項目中的方法掩完,總結如下:
百分百好用的獲取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
雖然無視版本百分百好用,但是如果你的手機有SD卡槽卻沒插SD卡硼砰,該方法最后 path 返回的是 null 且蓬,也就是說該方法無法判斷到底是沒插SD卡還是根本不支持SD卡
其實還有一種方法
String path = System.getenv("SECONDARY_STORAGE");
該方法只要你手機支持SD卡,無論你插沒插SD卡题翰,均會返回SD卡路徑恶阴,但是6.0及以上該方法被移除
Environment中源碼其實就是根據這個方法獲取路徑的
安卓官方文檔大家一定要看,他就是我們開發(fā)者的權威呀豹障,圣經呀7胧隆!