可能遇到的問題
android系統(tǒng)自身自帶有存儲(chǔ)馁菜,另外也可以通過(guò)sd卡來(lái)擴(kuò)充存儲(chǔ)空間贡珊。前者好比pc中的硬盤最爬,后者好移動(dòng)硬盤。 前者空間較小门岔,后者空間大爱致,但后者不一定可用。 開發(fā)應(yīng)用寒随,處理本地?cái)?shù)據(jù)存取時(shí)糠悯,可能會(huì)遇到這些問題:
- 需要判斷sd卡是否可用: 占用過(guò)多機(jī)身內(nèi)部存儲(chǔ),容易招致用戶反感妻往,優(yōu)先將數(shù)據(jù)存放于sd卡;
-
應(yīng)用數(shù)據(jù)存放路徑互艾,同其他應(yīng)用應(yīng)該保持一致,應(yīng)用卸載時(shí)讯泣,清除數(shù)據(jù):
標(biāo)新立異在sd卡根目錄建一個(gè)目錄纫普,招致用戶反感
用戶卸載應(yīng)用后,殘留目錄或者數(shù)據(jù)在用戶機(jī)器上好渠,招致用戶反感
需要判斷兩者的可用空間: sd卡存在時(shí)昨稼,可用空間反而小于機(jī)身內(nèi)部存儲(chǔ),這時(shí)應(yīng)該選用機(jī)身存儲(chǔ);
數(shù)據(jù)安全性拳锚,本應(yīng)用數(shù)據(jù)不愿意被其他應(yīng)用讀寫;
圖片緩存等假栓,不應(yīng)該被掃描加入到用戶相冊(cè)等媒體庫(kù)中去。
基本操作
- 使用外部存儲(chǔ)霍掺,需要的權(quán)限匾荆,在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開始,不再需要顯式聲明這兩個(gè)權(quán)限杆烁,除非要讀寫其他應(yīng)用的應(yīng)用數(shù)據(jù)($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;
}
}
存儲(chǔ)的用量情況
-
根據(jù)系統(tǒng)用戶不同牙丽,所能占用的存儲(chǔ)空間大小也有不同
在API level 9及其以上時(shí),
File
對(duì)象的getFreeSpace()
方法獲取系統(tǒng)root用戶可用空間连躏;
getUsableSpace()
取非root用戶可用空間 當(dāng)有多個(gè)存儲(chǔ)可用時(shí)獲取磁盤用量剩岳,根據(jù)當(dāng)前系統(tǒng)情況選用合適的存儲(chǔ)贞滨。
根據(jù)系統(tǒng)存儲(chǔ)用量入热,合理設(shè)定app所用的空間大小晓铆;運(yùn)行時(shí)勺良,也可做動(dòng)態(tài)調(diào)整。
在API level 9及其以上的系統(tǒng)骄噪,可直接調(diào)用
File
對(duì)象的相關(guān)方法尚困,以下需自行計(jì)算:
@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();
}
}
}
路徑的規(guī)律
一般地,通過(guò)Context 和
Environment`相關(guān)的方法獲取文件存取的路徑链蕊。
通過(guò)這兩個(gè)類可獲取各種路徑事甜,如圖:
($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()
|
+- ???
各個(gè)路徑的特性
下面介紹這些路徑的特性以及使用中需要注意的細(xì)節(jié):
-
根目錄(
$rootDir
):內(nèi)部存儲(chǔ)路徑:
/data
, 通過(guò)Environment.getDataDirectory()
獲取
外部存儲(chǔ)路徑:/storage/sdcard0
(也有類似/mnt/
這樣的),通過(guò)Environment.getExternalStorageDirectory()
獲取示例:
Environment.getDataDirectory(): /data Environment.getExternalStorageDirectory(): /storage/sdcard0
-
應(yīng)用數(shù)據(jù)目錄(
$appDataDir
)- 內(nèi)部?jī)?chǔ)存:
$appDataDir = $rootDir/data/$packageName
, - 外部存儲(chǔ):
$appDataDir = $rootDir/Andorid/data/$packageName
在這些目錄下的數(shù)據(jù)谬泌,在app卸載之后,會(huì)被系統(tǒng)刪除逻谦,我們應(yīng)將應(yīng)用的數(shù)據(jù)放于這兩個(gè)目錄中掌实。
- 內(nèi)部?jī)?chǔ)存:
-
外部存儲(chǔ)中,公開的數(shù)據(jù)目錄邦马。 這些目錄將不會(huì)隨著應(yīng)用的刪除而被系統(tǒng)刪除贱鼻,請(qǐng)斟酌使用:
Environment.getExternalStorageDirectory(): /storage/sdcard0 // 同 $rootDir Environment.getExternalStoragePublicDirectory(""): /storage/sdcard0 Environment.getExternalStoragePublicDirectory("folder1"): /storage/sdcard0/folder1
-
應(yīng)用數(shù)據(jù)目錄下的目錄
一般的在$appDataDir下,會(huì)有兩個(gè)目錄:
-
數(shù)據(jù)緩存:
$cacheDir = $appDataDir/cache
:- 內(nèi)部存儲(chǔ):
Context.getCacheDir()
, 機(jī)身內(nèi)存不足時(shí)滋将,文件會(huì)被刪除 - 外部存儲(chǔ):
Context.getExternalCacheDir()
外部存儲(chǔ)沒有實(shí)時(shí)監(jiān)控邻悬,當(dāng)空間不足時(shí),文件不會(huì)實(shí)時(shí)被刪除随闽,可能返回空對(duì)象
示例:
Context.getCacheDir(): /data/data/com.srain.cube.sample/cache Context.getExternalCacheDir(): /storage/sdcard0/Android/data/com.srain.cube.sample/cache
- 內(nèi)部存儲(chǔ):
-
文件目錄
$filesDir = $appDataDir/files
:- 內(nèi)部存儲(chǔ):通過(guò)
Context.getFilesDir()
獲取
Context.getFileStreamPath(String name)
返回以name
為文件名的文件對(duì)象父丰,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
- 外部存儲(chǔ):通過(guò)
Context.getExternalFilesDir(String type)
,type
為空字符串時(shí)獲取.
type
系統(tǒng)指定了幾種類型: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
- 內(nèi)部存儲(chǔ):通過(guò)
-
$cacheDir / $filesDir
安全性
在內(nèi)部存儲(chǔ)中掘宪,$cacheDir
, $filesDir
是app安全的础米,其他應(yīng)用無(wú)法讀取本應(yīng)用的數(shù)據(jù),而外部存儲(chǔ)則不是添诉。
在外部存儲(chǔ)中屁桑,這兩個(gè)文件夾其他應(yīng)用程序也可訪問。
在外部存儲(chǔ)中栏赴,$filesDir
中的媒體文件蘑斧,不會(huì)被當(dāng)做媒體掃描出來(lái),加到媒體庫(kù)中须眷。
-
$cacheDir / $filesDir
同級(jí)目錄
在內(nèi)部存儲(chǔ)中:通過(guò) Context.getDir(String name, int mode)
可獲取和 $filesDir / $cacheDir
同級(jí)的目錄
目錄的命名規(guī)則為 app_ + name
, 通過(guò)mode可控制此目錄為app私有還是其他app可讀寫竖瘾。
示例:
Context.getDir("dir1", MODE_PRIVATE):
Context.getDir: /data/data/com.srain.cube.sample/app_dir1
- 特別注意, 對(duì)于外部存儲(chǔ),獲取
$cacheDir
或者$filesDir
及其下的路徑
在API level 8 以下花颗,或者空間不足捕传,相關(guān)的方法獲路徑為空時(shí),需要自己構(gòu)造扩劝。
@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);
}