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存储空间,需要考虑以下诸多方面:

  1. 先判断SD卡是否可用,可用时优先使用SD卡的存储,不可用时用内部存储。
  2. 存储在SD卡上时,一般缓存文件放在应用程序内部,应用卸载时,清除数据;用户主动保存的文件放在SD卡上的其他文件夹里。
  3. 安全性较高&不愿被其他应用读取的数据,应放在内部存储,防止其他应用读取。
  4. 图片等缓存数据应放在外部存储应用数据目录(如:外部数据目录的files子目录)中,防止被扫描到用户相册等媒体库中。
  5. 清空缓存时,需要清空的数据放到cache目录中。

常见的招致用户反感的操作:

  1. 在SD卡上任意新建目录存放所有数据,用户卸载时会残存大量文件,招致用户反感。
  2. 图片缓存被扫描到用户的相册等媒体库中,招致用户反感。

存储路径获取

一般我们可以通过 Context 和 Environment 的方法获取文件存取的路径。Context获取应用程序相关的目录,Environment获取手机相关目录。

内存存储

  1. 根目录(rootDir):/data,通过 Environment.getDataDirectory() 获取。
  2. 应用程序目录(applicationDir)rootDir/data/包名(不一定,不同设备可能不同,也有可能是/user/0/)
  3. 应用缓存目录applicationDir/cache,通过Context.getCacheDir()获取,清除缓存会清空。
  4. 应用文件目录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

外部存储

  1. 根目录(rootDir):/storage/emulated/0(不一定,不同设备可能不同 ,也有类似 /mnt/ 这样的),通过 Environment.getExternalStorageDirectory() 获取。
  2. 应用程序目录(applicationDir)rootDir/Andorid/data/包名
  3. 应用缓存目录applicationDir/cache,通过Context.getExternalCacheDir()获取,清除缓存会清空。
  4. 应用文件目录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,中的媒体文件不会被当做媒体扫描出来,加到媒体库中。

一般在应用数据目录下会有两个目录(内部,外部皆有):

  1. 数据缓存(cache):系统-应用-清空缓存时,会被删除。
Context.getCacheDir();			 		//内部存储:清空缓存或机身内存不足时,文件会被删除
Context.getExternalCacheDir();		//外部存储:清空缓存时,文件会被删除。空间不足时,文件不会被删除,可能返回空对象
  1. 文件目录(files):
Context.getFilesDir();						//内部存储
Context.getFileStreamPath(“”);		//内部存储
Context.getExternalFilesDir(""):      //外部存储,不会被当做媒体扫描出来,加到媒体库中。

基本操作

  1. 判断 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;
    }
}
  1. 存储的用量情况
    根据系统用户不同,所能占用的存储空间大小也有不同。当有多个存储可用时获取磁盘用量,根据当前系统情况选用合适的存储。
    在 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();
        }
    }
}

参考:

  1. https://www.liaohuqiu.net/cn/posts/storage-in-android/