標籤:
可能遇到的問題
android系統自身內建有儲存,另外也可以通過sd卡來擴充儲存空間。前者好比pc中的硬碟,後者好移動硬碟。 前者空間較小,後者空間大,但後者不一定可用。 開發應用,處理本機資料存取時,可能會遇到這些問題:
- 需要判斷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(): /dataEnvironment.getExternalStorageDirectory(): /storage/sdcard0
-
應用資料目錄($appDataDir)
- 內部儲存:
$appDataDir = $rootDir/data/$packageName,
- 外部儲存:
$appDataDir = $rootDir/Andorid/data/$packageName
在這些目錄下的資料,在app卸載之後,會被系統刪除,我們應將應用的資料放於這兩個目錄中。
-
外部儲存中,公開的資料目錄。 這些目錄將不會隨著應用的刪除而被系統刪除,請斟酌使用:
Environment.getExternalStorageDirectory(): /storage/sdcard0// 同 $rootDirEnvironment.getExternalStoragePublicDirectory(""): /storage/sdcard0Environment.getExternalStoragePublicDirectory("folder1"): /storage/sdcard0/folder1
-
應用資料目錄下的目錄
一般的在$appDataDir下,會有兩個目錄:
-
資料緩衝:$cacheDir = $appDataDir/cache:
- 內部儲存:
Context.getCacheDir(), 機身記憶體不足時,檔案會被刪除
-
外部儲存:Context.getExternalCacheDir()
外部儲存沒有即時監控,當空間不足時,檔案不會即時被刪除,可能返回Null 物件
樣本:
Context.getCacheDir(): /data/data/com.srain.cube.sample/cacheContext.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/filesContext.getFileStreamPath(""): /data/data/com.srain.cube.sample/filesContext.getFileStreamPath("file1"): /data/data/com.srain.cube.sample/files/file1
-
外部儲存:通過Context.getExternalFilesDir(String type), type為空白字串時擷取.
type系統指定了幾種類型:
Environment.DIRECTORY_MUSICEnvironment.DIRECTORY_PICTURES...
樣本:
Context.getExternalFilesDir(""): /storage/sdcard0/Android/data/com.srain.cube.sample/filesContext.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);}
相關代碼:
https://github.com/liaohuqiu/cube-sdk/blob/master/core/src/in/srain/cube/diskcache/FileUtils.java
Android檔案儲存體使用參考