Android中的內部存儲與外部存儲

轉載:http://www.lxweimin.com/p/ebca517ae7d5
   http://blog.csdn.net/u012702547/article/details/50269639
   http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/0923/1557.html

該文章只是用于個人學習記錄,里面有些部分是直接轉載別人的,所以寫的比較亂,建議大家還是從原作者的博客去學習,同時尊重原作者的著作權,所轉載內容的鏈接在上面。

簡單介紹:

內部存儲空間中的應用私有目錄

對于設備中每一個安裝的 App,系統都會在內部存儲空間的 data/data 目錄下以應用包名為名字自動創建與之對應的文件夾。這個文件夾用于 App 中的 WebView 緩存頁面信息,SharedPreferences 和 SQLiteDatabase 持久化應用相關數據等。

對于沒有 Root 過的手機,普通用戶是無法查看 data/data 目錄內容的。不過開發人員可以使用模擬器調試應用,并通過 DDMS(Dalvik Debug Monitor Server)提供的 File Explorer 工具查看模擬器設備的存儲空間。

操作路徑:

第一步:Android Studio --> Tools --> Android --> Android Device Monitor;

第二步:Window --> Show View --> Android --> File Explorer。

Android SDK 提供有如下方法可以獲取并操作內部存儲空間下應用私有目錄文件的方,都位于 Application Context 中,供開發者直接調用:

getFilesDir():

getCacheDir():

deleteFile()

fileList()

等等,也可以通過 Environment 類訪問:
Environment.getDataDirectory();
注意:當用戶卸載 App 時,系統自動刪除 data/data 目錄下對應包名的文件夾及其內容。

外部存儲空間中的應用私有目錄
考慮內部存儲空間容量有限,普通用戶不能直接直觀地查看目錄文件等其他原因,Android 在外部存儲空間中也提供有特殊目錄供應用存放私有文件,文件路徑為:

/storage/emulated/0/Android/data/app package name

備注:一般設備都有內置 SD 卡,同時也提供外部 SD 卡拓展,可能對應路徑的目錄名有所差異。

值得注意的是,與內部存儲空間的應用私有目錄不同的是:

第一,默認情況下,系統并不會自動創建外部存儲空間的應用私有目錄。只有在應用需要的時候,開發人員通過 SDK 提供的 API 創建該目錄文件夾和操作文件夾內容。

第二,自 Android 7.0 開始,系統對應用私有目錄的訪問權限進一步限制。其他 App 無法通過 file:// 這種形式的 Uri 直接讀寫該目錄下的文件內容,而是通過 FileProvider 訪問。

第三,宿主 App 可以直接讀寫內部存儲空間中的應用私有目錄;而在 4.4 版本開始,宿主 App 才可以直接讀寫外部存儲空間中的應用私有目錄,使開發人員無需在 Manifest 文件中或者動態申請外部存儲空間的文件讀寫權限。

而相同點在于:同屬于應用私有目錄,當用戶卸載 App 時,系統也會自動刪除外部存儲空間下的對應 App 私有目錄文件夾及其內容。

同樣,Android SDK 中也提供有便捷的 API 供開發人員直接操作外部存儲空間下的應用私有目錄:

getExternalFilesDir()

getExternalCacheDir()

等等,當然,也可以通過 Environment 類間接操作,只不過需要向用戶申請操作權限:

Environment.getExternalStorageDirectory();

類似于 File 和 Cache 默認分類目錄,開發人員也可以在應用私有目錄中創建屬于自己的自定義目錄,方便于分類存儲應用相關文件。

值得注意的一點是,對于外部存儲空間下的應用私有目錄文件,由于普通用戶可以自由修改和刪除,開發人員在使用時,一定要做好判空處理和異常捕獲,防止應用崩潰退出!

外部存儲空間中的公共目錄
通常來說,應用涉及到的持久化數據分為兩類:應用相關數據和應用無關數據。前者是指專供宿主 App 使用的數據信息,比如一些應用的配置信息,數據庫信息,緩存文件等。當應用被卸載,這些信息也應該被隨之刪除,避免存儲空間產生不必要的占用。

相對而言,后者更偏向于這類信息:當應用被卸載,用戶仍然希望保留于設備當中的信息。常見如,拍照類應用的圖片文件,用戶是使用瀏覽器手動下載的文件等。

顯然,無論是內部存儲空間,還是外部儲存空間,上述兩個應用私有目錄由于其特有的生命周期(隨著應用卸載而自動清除)只適合存儲應用相關數據。

或者從訪問權限上來說,應用無關數據應該是宿主應用希望與其他應用共享這些數據的,應該存放在外部存儲空間的公共目錄文件夾下。

外部存儲空間已經為用戶默認分類出一些公共目錄。開發人員可以通過 Environment 類提供的方法直接獲取相應目錄的絕對路徑,傳遞不同的 type 參數類型即可:

Environment.getExternalStoragePublicDirectory(String type);

Envinonment 類提供諸多 type 參數的常量,比如:

DIRECTORY_MUSIC:Music

DIRECTORY_MOVIES:Movies

DIRECTORY_PICTURES:Pictures

DIRECTORY_DOWNLOADS:Download

等等,以第一個常量為例,音樂類別的公共目錄絕對路徑為:/storage/emulated/0/Music。如果你使用文件管理器打開設備的外部存儲空間的話,均可以看到這些公共目錄文件夾。

面對如此諸多的默認類別,開發人員在保存自己應用的公共文件時,也要養成良好的習慣,將要保存的數據分門別類地保存在不同公共目錄下。當然,你也可以在公共目錄下再次創建屬于自己應用的目錄,便于管理。
** 注意:訪問外部存儲空間時記得申請讀寫權限! **

** 外部存儲空間中的其他目錄 **
一般來說,利用兩種應用私有目錄和公共目錄便能夠存儲應用中需要保存的數據和文件。如果這些還不夠的話,那一定是你的開發姿勢不對。在 Code Review 的前提下,如果還是不夠的話,還可以在外部存儲空間自由創建其他目錄,通過這個方式獲取外部存儲空間的絕對路徑,然后操作文件:

Environment.getExternalStorageDirectory();

小結
使用應用私有目錄保存應用相關數據,使用公共目錄保存應用無關數據(共享數據)。無論哪種情況,都需要做好數據分類保存,便于清除等統一管理。隨便打開手機上的幾個應用,不難發現,很多應用都包含一個清理緩存的功能。事實上,開發人員清理的就是應用相關數據,也就是應用私有目錄下的文件。

考慮到外部存儲空間上的內容可能被用戶手動刪除,或者卸載拓展 SD 卡等不可控因素,操作前記得使用 Environment 類提供的 API 方法判斷容量是否充足、文件是否存在等情況,做好異常捕獲,減少應用崩潰率。相信這一定是一個良好的習慣。

詳細介紹:

內存,我們在英文中稱作memory,內部存儲,我們稱為InternalStorage,外部存儲我們稱為ExternalStorage。注意內部存儲不是內存。從用戶角度來說SD卡有內置和外置之分,但是對于開發者,只有內部存儲和外部存儲,內置SD卡和外置SD卡都屬于外部存儲范疇。

打開DDMS


這里有三個文件夾需要我們重視,一個是data,一個是mnt,一個是storage,我們下面就詳細說說這三個文件夾。

1.內部存儲

data文件夾就是我們常說的內部存儲,當我們打開data文件夾之后(沒有root的手機不能打開該文件夾),里邊有兩個文件夾值得我們關注,如下:



一個文件夾是app文件夾,還有一個文件夾就是data文件夾,app文件夾里存放著我們所有安裝的app的apk文件,其實,當我們調試一個app的時候,可以看到控制臺輸出的內容,有一項是uploading .....就是上傳我們的apk到這個文件夾,上傳成功之后才開始安裝。另一個重要的文件夾就是data文件夾了,這個文件夾里邊都是一些包名,打開這些包名之后我們會看到這樣的一些文件:
1.data/data/包名/shared_prefs2.data/data/包名/databases3.data/data/包名/files
4.data/data/包名/cache
如果打開過data文件,應該都知道這些文件夾是干什么用的,我們在使用sharedPreferenced的時候,將數據持久化存儲于本地,其實就是存在這個文件中的xml文件里,我們App里邊的數據庫文件就存儲于databases文件夾中,還有我們的普通數據存儲在files中,緩存文件存儲在cache文件夾中,存儲在這里的文件我們都稱之為內部存儲。

3.外部存儲

外部存儲才是我們平時操作最多的,外部存儲一般就是我們上面看到的storage文件夾,當然也有可能是mnt文件夾,這個不同廠家有可能不一樣。
一般來說,在storage文件夾中有一個sdcard文件夾,這個文件夾中的文件又分為兩類,一類是公有目錄,還有一類是私有目錄,其中的公有目錄有九大類,比如DCIM、DOWNLOAD等這種系統為我們創建的文件夾,私有目錄就是Android這個文件夾,這個文件夾打開之后里邊有一個data文件夾,打開這個data文件夾,里邊有許多包名組成的文件夾

外部存儲中的文件是可以被用戶或者其他應用程序修改的,有兩種類型的文件(或者目錄):
1.公共文件Public files:文件是可以被自由訪問,且文件的數據對其他應用或者用戶來說都是由意義的,當應用被卸載之后,其卸載前創建的文件仍然保留。比如camera應用,生成的照片大家都能訪問,而且camera不在了,照片仍然在。
2.私有文件Private files:其實由于是外部存儲的原因即是是這種類型的文件也能被其他程序訪問,只不過一個應用私有的文件對其他應用其實是沒有訪問價值的(惡意程序除外)。外部存儲上,應用私有文件的價值在于卸載之后,這些文件也會被刪除。類似于內部存儲。因為這些文件也會在應用刪除的時候跟隨這一起刪掉~只是和內部儲存不同的是這個部分可以給用戶和其他應用訪問。所以才叫外部儲存的私有部分嘛。
所有應用程序的外部存儲的私有文件都放在根目錄的Android/data/下,目錄形式為/Android/data/<package_name>/

3.操作存儲空間

首先,經過上面的分析,大家已經明白了,什么是內部存儲,什么是外部存儲,以及這兩種存儲方式分別存儲在什么位置,一般來說,我們不會自己去操作內部存儲空間,沒有root權限的話,我們也沒法操作內部存儲空間,事實上內部存儲主要是由系統來維護的。不過在代碼中我們是可以訪問到這個文件夾的。由于內部存儲空間有限,在開發中我們一般都是操作外部存儲空間,Google官方建議我們App的數據應該存儲在外部存儲的私有目錄中該App的包名下,這樣當用戶卸載掉App之后,相關的數據會一并刪除,如果你直接在/storage/sdcard目錄下創建了一個應用的文件夾,那么當你刪除應用的時候,這個文件夾就不會被刪除。
經過以上的介紹,我們可以總結出下面一個表格:



如果按照路徑的特征,我們又可以將文件存儲的路徑分為兩大類,一類是路徑中含有包名的,一類是路徑中不含有包名的,含有包名的路徑,因為和某個App有關,所以對這些文件夾的訪問都是調用Context里邊的方法,而不含有包名的路徑,和某一個App無關,我們可以通過Environment中的方法來訪問。如下圖:



說到這里,我想大家對內部存儲、外部存儲該有了一個清晰的認識了吧。我們在開發中,不建議往內部存儲中寫太多的數據,畢竟空間有限。外部存儲在使用的時候最好能夠將文件存放在私有目錄下,這樣有利于系統維護,也避免用戶的反感。

操作權限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

內部存儲操作:

private void createPrivateFile()//向內部存儲寫文件
{    
    String filename = "myfile.txt";    
    String string = "Hello world!";   
    FileOutputStream outputStream;    
      try {      
          outputStream = openFileOutput(filename, Context.MODE_PRIVATE);                
          outputStream.write(string.getBytes());    
          outputStream.close();      
          Toast.makeText(MainActivity.this,"文件位置    在"+getFilesDir().getAbsolutePath(),Toast.LENGTH_LONG).show();   
       } catch (Exception e) 
    {       
     e.printStackTrace();     
     Toast.makeText(MainActivity.this,"出錯誤了哦",Toast.LENGTH_SHORT).show();   
     }
}
//列出所有的已創建的文件,這個可能不容易想到,Context居然有這樣的方法。
String[] files = Context.fileList();
for(String file : files) {
Log.e(TAG, "file is "+ file);
}
//刪除文件,能創建就要能夠刪除,當然也會提供了刪除文件的接口,它也非常簡單,只需要提供文件名
f(Context.deleteFile(filename)) {
Log.e(TAG, "delete file "+ filename + " sucessfully“);
} else {
Log.e(TAG, "failed to deletefile " + filename);
}

外部存儲操作
由于外部存儲可能不可用—比如,當用戶已將存儲裝載到電腦或已移除提供外部存儲的 SD 卡時—因此,在訪問它之前,您應始終確認其容量。 您可以通過調用 getExternalStorageState() 查詢外部存儲狀態。 如果返回的狀態為 MEDIA_MOUNTED,那么您可以對您的文件進行讀寫。 例如,以下方法對于確定存儲可用性非常有用:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

公共文件
應供其他應用和用戶自由使用的文件。 當用戶卸載您的應用時,用戶應仍可以使用這些文件。
例如,您的應用拍攝的照片或其他已下載的文件。

私有文件
屬于您的應用且在用戶卸載您的應用時應予刪除的文件。 盡管這些文件在技術上可被用戶和其他應用訪問(因為它們存儲在外部存儲中), 但它們實際上不向您的應用之外的用戶提供任何輸出值。 當用戶卸載您的應用時,系統會刪除應用外部私有目錄中的所有文件。
例如,您的應用下載的其他資源或臨時介質文件。

如果您要將公共文件保存在外部存儲設備上,請使用 getExternalStoragePublicDirectory() 方法獲取表示外部存儲設備上相應目錄的 File。 該方法使用指定您想要保存以便它們可以與其他公共文件在邏輯上組織在一起的文件類型的參數,比如 DIRECTORY_MUSIC 或 DIRECTORY_PICTURES。例如:

public File getAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory.
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

如果您要保存您的應用專用文件,您可以通過調用 getExternalFilesDir()并向其傳遞指示您想要的目錄類型的名稱,從而獲取相應的目錄。通過這種方法創建的各個目錄將添加至封裝您的應用的所有外部存儲文件的父目錄,當用戶卸載您的應用時,系統會刪除這些文件。

public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory.
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

如果沒有適合您文件的預定義子目錄名稱,您可以改為調用 getExternalFilesDir() 并傳遞 null。這將返回外部存儲上您的應用的專用目錄的根目錄。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,947評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,094評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,268評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,817評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,039評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,551評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,502評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,662評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,907評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,255評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,637評論 2 370

推薦閱讀更多精彩內容