存儲分類:
內部存儲路徑,
內部緩存存儲路徑,
外部存儲路徑,
外部緩存存儲路徑
在有些手機上內部划出一個內部的sdcard路徑和內部存儲路徑,當有sdcard時候,就有了六個路徑
內部存儲空間中的應用私有目錄
對於設備中每一個安裝的 App,系統都會在內部存儲空間的 data/data 目錄下以應用包名為名字自動創建與之對應的文件夾。這個文件夾用於 App 中的 WebView 緩存頁面信息,SharedPreferences 和 SQLiteDatabase 持久化應用相關數據等。
對於沒有 Root 過的手機,普通用戶是無法查看 data/data 目錄內容的。不過開發人員可以使用模擬器調試應用,並通過 DDMS(Dalvik Debug Monitor Server)提供的 File Explorer 工具查看模擬器設備的存儲空間。
用戶卸載 App 時,系統自動刪除 data/data 目錄下對應包名的文件夾及其內容
- context.getCacheDir()
這個目錄和getFilesDir()目錄最大的不同在於:當安卓設備的存儲空間少,或者不夠用的時候,系統會自動刪除這個目錄下的文件。
- context.getFilesDir()
當應用被移動到外部存儲設備的時候,文件的絕對路徑也是變化的,所以建議當數據存儲到這個目錄的時候,用相對路徑。系統提供的訪問此路徑文件的方法是:context.openFileOutput(String,int);context.openFileInput(String name);
內部存儲空間不需要申請權限
- context.deleteFile
外部存儲空間中的應用私有目錄
考慮內部存儲空間容量有限,(官方建議是,超過1MB的文件,建議存儲到getExternalCacheDir()目錄下)普通用戶不能直接直觀地查看目錄文件等其他原因,Android 在外部存儲空間中也提供有特殊目錄供應用存放私有文件,文件路徑為:
/storage/emulated/0/Android/data/包名
備注:一般設備都有內置 SD 卡,同時也提供外部 SD 卡拓展,可能對應路徑的目錄名有所差異。
值得注意的是,與內部存儲空間的應用私有目錄不同的是:
- 第一,默認情況下,系統並不會自動創建外部存儲空間的應用私有目錄。只有在應用需要的時候,開發人員通過 SDK 提供的 API 創建該目錄文件夾和操作文件夾內容。
- 第二,自 Android 7.0 開始,系統對應用私有目錄的訪問權限進一步限制。其他 App 無法通過 file:// 這種形式的 Uri 直接讀寫該目錄下的文件內容,而是通過 FileProvider 訪問。
- 第三,宿主 App 可以直接讀寫內部存儲空間中的應用私有目錄;而在 4.4 版本開始,宿主 App 才可以直接讀寫外部存儲空間中的應用私有目錄,使開發人員無需在 Manifest 文件中或者動態申請外部存儲空間的文件讀寫權限。
而相同點在於:同屬於應用私有目錄,當用戶卸載 App 時,系統也會自動刪除外部存儲空間下的對應 App 私有目錄文件夾及其內容。
只有手機系統使用的是虛擬外部存儲(虛擬SD卡,現在絕大多數的手機,都不用外掛物理SD卡了)的時候,
才可以在卸載應用的同時,自動刪除該目錄下的文件,如果是之前的物理存儲(物理SD卡)則不會自動刪除該目錄,及目錄下的文件。
Android SDK 中也提供有便捷的 API 供開發人員直接操作外部存儲空間下的應用私有目錄:
- getExternalFilesDir()
- getExternalCacheDir()
當然,也可以通過 Environment 類間接操作,只不過需要向用戶申請操作權限
Environment.getExternalStorageDirectory();
類似於 File 和 Cache 默認分類目錄,開發人員也可以在應用私有目錄中創建屬於自己的自定義目錄,方便於分類存儲應用相關文件。
關於Environment.getExternalStorageDirectory();做一些說明:
應用外部存儲空間(數據文件非私有,可以被手機的系統程序訪問(如MP3格式的文件,會被手機系統檢索出來),同樣,該目錄下的文件,所有的APP程序也都是可以訪問的,)
注意:外部存儲空間可能處於不可訪問狀態,或者已經被移除狀態,或者存儲空間損壞無法訪問等問題。可以通過getExternalStorageState()這個方法來判斷外部存儲空間的狀態。
注:在該目錄下讀寫文件,需要獲取讀寫權限
該目錄下的文件,這個目錄是用戶進行操作的一個根目錄,進入二級目錄可以通過
getExternalFilesDirs(String), getExternalCacheDirs(), and getExternalMediaDirs().這些方法
官方建議,不要直接使用該目錄,為了避免污染用戶的根命名空間,應用私有的數據,應該放在 Context.getExternalFilesDir目錄下
其他的可以被分享的文件,可以放在getExternalStoragePublicDirectory(String).目錄下
值得注意的一點是,對於外部存儲空間下的應用私有目錄文件,由於普通用戶可以自由修改和刪除,開發人員在使用時,一定要做好判空處理和異常捕獲,防止應用崩潰退出!
外部存儲空間中的公共目錄
通常來說,應用涉及到的持久化數據分為兩類:應用相關數據和應用無關數據。前者是指專供宿主 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();
小結
* 記住外部是data/data/包名目錄,外部是Android/data/包名,應用私有目錄,卸載app,系統自動刪除應用私有目錄,內部外部,是從系統的角度來說的 *
使用應用私有目錄保存應用相關數據,使用公共目錄保存應用無關數據(共享數據)。無論哪種情況,都需要做好數據分類保存,便於清除等統一管理。隨便打開手機上的幾個應用,不難發現,很多應用都包含一個清理緩存的功能。事實上,開發人員清理的就是應用相關數據,也就是應用私有目錄下的文件。
考慮到外部存儲空間上的內容可能被用戶手動刪除,或者卸載拓展 SD 卡等不可控因素,操作前記得使用 Environment 類提供的 API 方法判斷容量是否充足、文件是否存在等情況,做好異常捕獲,減少應用崩潰率。相信這一定是一個良好的習慣
存儲路徑和緩存存儲路徑的區別
1. 緩存路徑一般用於存放一些緩存數據,當程序刪除的時候,緩存文件夾也會隨之刪除,避免了程序在卸載之后殘留大量文件。
2. 內部緩存路徑和外部緩存路徑也有所區別,當系統的內存空間緊張時,內部緩存路徑下的文件會被刪除,但是沒有一個嚴格的標准保障,應該對這些緩存文件占用的最大存儲空間設定個最大值,當實際超過這個值時,要對這些緩存文件做相應的清理工作 。但是系統不會觀察外部緩存路徑下是否超出大小,要自己去做文件清理機制。
3. 內部緩存路徑默認別的程序不能訪問文件,所以一些私密的數據,別的應用是訪問不了的,如果需要系統或者其他應用訪問,可以通過修改openFileOutput()下的int mode參數讓別的應用也可以訪問。
* 內部緩存路徑 *
內部的 /data/data/一般是看不到的,除非root
清除緩存 context.getCacheDir()
模擬器:內部緩存路徑:/data/data/com.mocn.testin/cache/內部緩存路徑.png
Nexus5:內部緩存路徑:/data/user/0/com.mocn.testin/cache/內部緩存路徑.png
log
04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getRootDirectory()=:/system 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getDataDirectory()=:/data 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication I/mrpeng: Environment.getDataDirectory().getAbsolutePath()/data 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getDownloadCacheDirectory()=:/cache 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getExternalStorageDirectory()=:/storage/emulated/0 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getExternalStoragePublicDirectory()=:/storage/emulated/0/Pictures 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.getExternalStorageState()=:mounted 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.isExternalStorageEmulated()=:true 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: Environment.isExternalStorageRemovable()=</span>:false 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getFilesDir()=:/data/data/com.example.bafc.myapplication/files 04-09 00:03:23.821 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getCacheDir()=:/data/data/com.example.bafc.myapplication/cache 04-09 00:03:23.841 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getExternalFilesDir()=:/storage/emulated/0/Android/data/com.example.bafc.myapplication/files/Movies 04-09 00:03:23.841 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getExternalCacheDir()=:/storage/emulated/0/Android/data/com.example.bafc.myapplication/cache 04-09 00:03:23.841 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getPackageResourcePath()=:/data/app/com.example.bafc.myapplication-2.apk 04-09 00:03:23.841 13714-13714/com.example.bafc.myapplication E/mrpeng: context.getDatabasePath("mufeng")=:/data/data/com.example.bafc.myapplication/databases/mufeng