android系统自身自带有存储,另外也可以通过sd卡来扩充存储空间。前者好比pc中的硬盘,后者好移动硬盘。 前者空间较小,后者空间大,但后者不一定可用。 开发应用,处理本地数据存取时,可能会遇到这些问题:
<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 )
/**
* 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用户可用空间
@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.myapp
| |
| | ($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.myapp
|
| ($filesDir)
+- files -> Context.getExternalFilesDir("")
| |
| +- file1 -> Context.getExternalFilesDir("file1")
| +- Music -> Context.getExternalFilesDir(Environment.Music);
| +- Picture -> ... Environment.Picture
| +- ...
|
| ($cacheDir)
+- cache -> Context.getExternalCacheDir()
|
+- ???
下面介绍这些路径的特性以及使用中需要注意的细节:
示例:
Environment.getDataDirectory():
/data
Environment.getExternalStorageDirectory():
/storage/sdcard0
$appDataDir
目录下的数据,在app卸载之后,会被系统删除,我们应将应用的数据放于这两个目录中。
Environment.getExternalStorageDirectory():
/storage/sdcard0
// 同 $rootDir
Environment.getExternalStoragePublicDirectory(""):
/storage/sdcard0
Environment.getExternalStoragePublicDirectory("folder1"):
/storage/sdcard0/folder1
示例:
Context.getCacheDir():
/data/data/com.myapp/cache
Context.getExternalCacheDir():
/storage/sdcard0/Android/data/com.myapp/cache
2. 文件目录 $filesDir = $appDataDir/files :
* 内部存储:通过 Context.getFilesDir() 获取
> Context.getFileStreamPath(String name) 返回以 name 为文件名的文件对象, name 为空,则返回 $filesDir 本身
示例:
Context.getFilesDir():
/data/data/com.myapp/files
Context.getFileStreamPath(""):
/data/data/com.myapp/files
Context.getFileStreamPath("file1"):
/data/data/com.myapp/files/file1
* 外部存储:通过 Context.getExternalFilesDir(String type) , type 为空字符串时获取.
type 系统指定了几种类型:
```
Environment.DIRECTORY_MUSIC
Environment.DIRECTORY_PICTURES
...
```
示例:
```
Context.getExternalCacheDirs():
/storage/sdcard0/Android/data/com.myapp/files
Context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
/storage/sdcard0/Android/data/com.myapp/files/Music
```
3. $cacheDir / $filesDir 安全性
* 在内部存储中, $cacheDir , $filesDir 是app安全的,其他应用无法读取本应用的数据,而外部存储则不是。 * 在外部存储中,这两个文件夹其他应用程序也可访问。 * 在外部存储中, $filesDir 中的媒体文件,不会被当做媒体扫描出来,加到媒体库中。
4. $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
5. 特别注意, 对于外部存储,获取 $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);
}