轉自:https://www.liaohuqiu.net/cn/posts/storage-in-android/
Android存儲使用參考
15 Jan 2014
可能遇到的問題
android系統自身自帶有存儲,另外也可以通過sd卡來擴充存儲空間。前者好比pc中的硬盤,後者好移動硬盤。 前者空間較小,後者空間大,但後者不一定可用。 開發應用,處理本地數據存取時,可能會遇到這些問題:
- 需要判斷sd卡是否可用: 佔用過多機身內部存儲,容易招致用戶反感,優先將數據存放於sd卡;
-
應用數據存放路徑,同其他應用應該保持一致,應用卸載時,清除數據:
- 標新立異在sd卡根目錄建一個目錄,招致用戶反感
- 用戶卸載應用後,殘留目錄或者數據在用戶機器上,招致用戶反感
-
需要判斷兩者的可用空間: sd卡存在時,可用空間反而小於機身內部存儲,這時應該選用機身存儲;
-
數據安全性,本應用數據不願意被其他應用讀寫;
-
圖片緩存等,不應該被掃描加入到用戶相冊等媒體庫中去。
基本操作
-
使用外部存儲,需要的權限,在
AndoridManifest.xml
中:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
從API 19 / Andorid 4.4 / KITKAT開始,不再需要顯式聲明這兩個權限,除非要讀寫其他應用的應用數據(
$appDataDir
) -
判斷sd卡可用:
/** * Check if the primary "external" storage device is available. * * @return */ public static boolean hasSDCardMounted() { String state = Environment.getExternalStorageState(); if (state != null && state.equals(Environment.MEDIA_MOUNTED)) { return true; } else { return false; } }
存儲的用量情況
-
根據系統用戶不同,所能佔用的存儲空間大小也有不同
在API level 9及其以上時,
File
對象的getFreeSpace()
方法獲取系統root用戶可用空間;getUsableSpace()
取非root用戶可用空間 -
當有多個存儲可用時獲取磁盤用量,根據當前系統情況選用合適的存儲。
-
根據系統存儲用量,合理設定app所用的空間大小;運行時,也可做動態調整。
-
在API level 9及其以上的系統,可直接調用
File
對象的相關方法,以下需自行計算:@TargetApi(VERSION_CODES.GINGERBREAD) public static long getUsableSpace(File path) { if (path == null) { return -1; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { return path.getUsableSpace(); } else { if (!path.exists()) { return 0; } else { final StatFs stats = new StatFs(path.getPath()); return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); } } }
路徑的規律
一般地,通過Context
和 Environment
相關的方法獲取文件存取的路徑。
通過這兩個類可獲取各種路徑,如圖:
($rootDir)
+- /data -> Environment.getDataDirectory()
| |
| | ($appDataDir)
| +- data/com.srain.cube.sample
| |
| | ($filesDir)
| +- files -> Context.getFilesDir() / Context.getFileStreamPath("")
| | |
| | +- file1 -> Context.getFileStreamPath("file1")
| | ($cacheDir)
| +- cache -> Context.getCacheDir()
| |
| +- app_$name ->(Context.getDir(String name, int mode)
|
| ($rootDir)
+- /storage/sdcard0 -> Environment.getExternalStorageDirectory()
| / Environment.getExternalStoragePublicDirectory("")
|
+- dir1 -> Environment.getExternalStoragePublicDirectory("dir1")
|
| ($appDataDir)
+- Andorid/data/com.srain.cube.sample
|
| ($filesDir)
+- files -> Context.getExternalFilesDir("")
| |
| +- file1 -> Context.getExternalFilesDir("file1")
| +- Music -> Context.getExternalFilesDir(Environment.Music);
| +- Picture -> ... Environment.Picture
| +- ...
|
| ($cacheDir)
+- cache -> Context.getExternalCacheDir()
|
+- ???
各個路徑的特性
下面介紹這些路徑的特性以及使用中需要注意的細節:
-
根目錄(
$rootDir
):-
內部存儲路徑:
/data
, 通過Environment.getDataDirectory()
獲取 -
外部存儲路徑:
/storage/sdcard0
(也有類似 /mnt/ 這樣的),通過Environment.getExternalStorageDirectory()
獲取示例:
Environment.getDataDirectory(): /data Environment.getExternalStorageDirectory(): /storage/sdcard0
-
內部存儲路徑:
-
應用數據目錄(
$appDataDir
)-
內部儲存:
$appDataDir = $rootDir/data/$packageName
, -
外部存儲:
$appDataDir = $rootDir/Andorid/data/$packageName
在這些目錄下的數據,在app卸載之後,會被系統刪除,我們應將應用的數據放於這兩個目錄中。
-
內部儲存:
-
外部存儲中,公開的數據目錄。 這些目錄將不會隨着應用的刪除而被系統刪除,請斟酌使用:
Environment.getExternalStorageDirectory(): /storage/sdcard0 // 同 $rootDir Environment.getExternalStoragePublicDirectory(""): /storage/sdcard0 Environment.getExternalStoragePublicDirectory("folder1"): /storage/sdcard0/folder1
-
應用數據目錄下的目錄
一般的在$appDataDir下,會有兩個目錄:
-
數據緩存:
$cacheDir = $appDataDir/cache
:-
內部存儲:
Context.getCacheDir()
, 機身內存不足時,文件會被刪除 -
外部存儲:
Context.getExternalCacheDir()
外部存儲沒有實時監控,當空間不足時,文件不會實時被刪除,可能返回空對象
示例:
Context.getCacheDir(): /data/data/com.srain.cube.sample/cache Context.getExternalCacheDir(): /storage/sdcard0/Android/data/com.srain.cube.sample/cache
-
內部存儲:
-
文件目錄
$filesDir = $appDataDir/files
:-
內部存儲:通過
Context.getFilesDir()
獲取Context.getFileStreamPath(String name)
返回以name
爲文件名的文件對象,name
爲空,則返回$filesDir
本身示例:
Context.getFilesDir(): /data/data/com.srain.cube.sample/files Context.getFileStreamPath(""): /data/data/com.srain.cube.sample/files Context.getFileStreamPath("file1"): /data/data/com.srain.cube.sample/files/file1
-
外部存儲:通過
Context.getExternalFilesDir(String type)
,type
爲空字符串時獲取.type
系統指定了幾種類型:Environment.DIRECTORY_MUSIC Environment.DIRECTORY_PICTURES ...
示例:
Context.getExternalFilesDir(""): /storage/sdcard0/Android/data/com.srain.cube.sample/files Context.getExternalFilesDir(Environment.DIRECTORY_MUSIC) /storage/sdcard0/Android/data/com.srain.cube.sample/files/Music
-
-
$cacheDir / $filesDir
安全性在內部存儲中,
$cacheDir
,$filesDir
是app安全的,其他應用無法讀取本應用的數據,而外部存儲則不是。在外部存儲中,這兩個文件夾其他應用程序也可訪問。
在外部存儲中,
$filesDir
中的媒體文件,不會被當做媒體掃描出來,加到媒體庫中。 -
$cacheDir / $filesDir
同級目錄在內部存儲中:通過
Context.getDir(String name, int mode)
可獲取和$filesDir
/$cacheDir
同級的目錄目錄的命名規則爲
app_ + name
, 通過mode可控制此目錄爲app私有還是其他app可讀寫。示例:
Context.getDir("dir1", MODE_PRIVATE): Context.getDir: /data/data/com.srain.cube.sample/app_dir1
-
特別注意, 對於外部存儲,獲取
$cacheDir
或者$filesDir
及其下的路徑在API level 8 以下,或者空間不足,相關的方法獲路徑爲空時,需要自己構造。
@TargetApi(VERSION_CODES.FROYO) public static File getExternalCacheDir(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)) { File path = context.getExternalCacheDir(); // In some case, even the sd card is mounted, // getExternalCacheDir will return null // may be it is nearly full. if (path != null) { return path; } } // Before Froyo or the path is null, // we need to construct the external cache folder ourselves final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/"; return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir); }
-