Android系统的存储空间分为内部存储和外部存储。
存储类型 | 位置 | 空间大小 | 是否可用 | 是否需要声明 |
内部 | 手机系统自带的存储 | 空间较小 | 一直可用,剩余空间可能不足 | 不需要声明 |
外部 | 一般是sd卡存储 | 空间较大 | 不一定可用,剩余空间可能不足 | 需要在Androidmanifest.xml中显式声明 |
说明:外部存储在AndoridManifest.xml中显式声明: ( 4.4+,不再需要显式声明这两个权限,除非要读写其他应用的应用数据)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
如何使用Android存储空间,需要考虑以下诸多方面:
- 先判断SD卡是否可用,可用时优先使用SD卡的存储,不可用时用内部存储。
- 存储在SD卡上时,一般缓存文件放在应用程序内部,应用卸载时,清除数据;用户主动保存的文件放在SD卡上的其他文件夹里。
- 安全性较高&不愿被其他应用读取的数据,应放在内部存储,防止其他应用读取。
- 图片等缓存数据应放在外部存储应用数据目录(如:外部数据目录的files子目录)中,防止被扫描到用户相册等媒体库中。
- 清空缓存时,需要清空的数据放到cache目录中。
常见的招致用户反感的操作:
- 在SD卡上任意新建目录存放所有数据,用户卸载时会残存大量文件,招致用户反感。
- 图片缓存被扫描到用户的相册等媒体库中,招致用户反感。
存储路径获取
一般我们可以通过 Context 和 Environment 的方法获取文件存取的路径。Context获取应用程序相关的目录,Environment获取手机相关目录。
内存存储
- 根目录(rootDir):/data,通过 Environment.getDataDirectory() 获取。
- 应用程序目录(applicationDir):rootDir/data/包名(不一定,不同设备可能不同,也有可能是/user/0/)
- 应用缓存目录:applicationDir/cache,通过Context.getCacheDir()获取,清除缓存会清空。
- 应用文件目录:applicationDir/files,通过Context.getFilesDir()获取。Context.getFileStreamPath(String name)返回以name为文件名的子文件目录,若name为空,则返回文件根目录。
Environment.getDataDirectory(): /data
Context.getCacheDir(): /data/data/package/cache
Context.getFilesDir(): /data/data/package/files
Context.getFileStreamPath(""): /data/data/package/files
Context.getFileStreamPath("test"): /data/data/package/files/test
通过 Context.getDir(String name, int mode)可获取和 filesDir / cacheDir 同级的目录。目录的命名规则为 app_ + name, 通过 mode 可控制此目录为 app 私有还是其他 app 可读写。
Context.getDir("dir1", MODE_PRIVATE); /data/data/package/app_dir1
外部存储
- 根目录(rootDir):/storage/emulated/0(不一定,不同设备可能不同 ,也有类似 /mnt/ 这样的),通过 Environment.getExternalStorageDirectory() 获取。
- 应用程序目录(applicationDir):rootDir/Andorid/data/包名
- 应用缓存目录:applicationDir/cache,通过Context.getExternalCacheDir()获取,清除缓存会清空。
- 应用文件目录:applicationDir/files,通过Context.getExternalFilesDir(String type),type为空字符串时获取。
Environment.getExternalStorageDirectory(): /storage/emulated/0
Context.getExternalCacheDir(): /storage/emulated/0/Android/data/package/cache
Context.getExternalFilesDir(""): /storage/emulated/0/Android/data/package/files
Context.getExternalFilesDir("test"): /storage/emulated/0/Android/data/package/files/test
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Android/data/package/files/Pictures
我们可以在外部存储上新建任意文件夹,公开的数据目录。 这些目录将不会随着应用的删除而被系统删除,请斟酌使用:。不过在6.0及之后的系统需要动态申请权限。如:
Environment.getExternalStorageDirectory(): /storage/emulated/0
Environment.getExternalStoragePublicDirectory(""): /storage/emulated/0
Environment.getExternalStoragePublicDirectory("test"): /storage/emulated/0/test
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Pictures
type系统给我们提供了很多常用的类型,比如图片和下载等等:
public static String DIRECTORY_MUSIC = "Music";
public static String DIRECTORY_ALARMS = "Alarms";
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
public static String DIRECTORY_PICTURES = "Pictures";
public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_DCIM = "DCIM";
public static String DIRECTORY_DOCUMENTS = "Documents";
应用数据目录:
应用数据目录也包括内部存储和外部存储两部分,在这些目录下的数据,在 app 卸载或系统-应用-清除数据之后,会被系统删除,我们应将应用的数据放于这两个目录中。
内部储存: appDataDir = rootDir/data/packageName,cacheDir, filesDir是 app 安全的,其他应用无法读取本应用的数据。
外部存储: appDataDir = rootDir/Andorid/data/packageName,cacheDir, filesDir不是 app 安全的,这两个文件夹其他应用程序也可访问。filesDir,中的媒体文件不会被当做媒体扫描出来,加到媒体库中。
一般在应用数据目录下会有两个目录(内部,外部皆有):
- 数据缓存(cache):系统-应用-清空缓存时,会被删除。
Context.getCacheDir(); //内部存储:清空缓存或机身内存不足时,文件会被删除
Context.getExternalCacheDir(); //外部存储:清空缓存时,文件会被删除。空间不足时,文件不会被删除,可能返回空对象
- 文件目录(files):
Context.getFilesDir(); //内部存储
Context.getFileStreamPath(“”); //内部存储
Context.getExternalFilesDir(""): //外部存储,不会被当做媒体扫描出来,加到媒体库中。
基本操作
- 判断 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();
}
}
}
参考: