安卓內(nèi)外部存儲(chǔ)完全解析 -- 別再弄混了

安卓開發(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 部分:

  1. 機(jī)器自帶的存儲(chǔ),也就是手機(jī)廠商常說的16G,32G,64G之類的存儲(chǔ)空間

  2. 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)呀!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容