如果有错误欢迎指正。
1、简介
Android 提供了文件系统,统一管理手机内的各种文件。
站在应用的角度,文件主要分为三类:
- 系统文件,包括底层的 Linux 驱动文件等,用户不可见。[root 除外]
- 内部存储,是私有的存储空间,开发者可以通过 Android SDK 提供的 API 访问,用户不可见。[root 除外] 其他应用不能访问。
- 外部存储,用户可见。开发者可以通过 Android SDK 提供的 API 访问。其他应用不能访问。
站在用户角度,分为系统文件,应用私有文件,以及普通的文件。其中系统文件用户不可见,应用私有文件中,外部存储中的文件用户可见。
也就是说:
- 不可见的部分(内部存储),可以粗略分为两块:系统文件,以及应用的用户不可见私有文件;
- 可见的部分(外部存储),分为用户可见的应用私有文件以及一般的文件。
一个应用拥有的在两个地方(内部存储、外部存储)存储的文件,其他应用不可以访问(除非应用主动通过内容提供器暴露路径),因而被称为应用私有空间。
重点介绍的是应用层面的内部存储和外部存储两个部分。
2、内部存储
英文名称为 internal storage
。
可以通过 context
获取 API 访问,API 和功能如下:
getFilesDir()
获取 /data/data/<包名>/files
目录,返回 File 对象。
getDir()
获取 /data/data/<包名>
目录,返回 File 对象。
getCacheDir()
获取 /data/data/<包名>/cache
目录,返回 File 对象。
以上目录是专门为当前应用准备的,这部分对于用户来说不可见,不必申请权限。
当 app 删除时,应用内部存储对应的目录以及其下的文件都会被删除。
如果应用不需要与其他应用共享文件的话,以上几个 API 足够应付应用文件存储了。
3、外部存储
英文名称为 external storage
。
可以通过 context
获取 API 访问。
外部存储实际上指的一般是 sd 卡。现在安卓机上都有文件管理的 app,一般打开 app 看到的最外层目录列表就属于所谓外部存储,用户可以在这个目录下的任一位置新建、删除文件。
如下是 Pixel 手机提供的文件管理 app,可以看到的目录都属于外部存储。
根据官方文档描述,当应用运行在 Android 4.4(API Level 19)或更高版本的 Android 上时,不需要申请读写外存权限。
另外因为各个安卓厂商可以定制自己的系统,所以外存路径访问的 API 返回的路径并不是唯一的。

假设: 外部存储的根目录路径为 path
。
Android SDK 提供的 API 如下:
getExternalFilesDir(String type)
获取的是 path/Android/data/<包名>/files
路径的 File 对象。注意这个方法需要指定一个参数 type,返回的 File 的路径是 path/Android/data/<包名>/files/<type>
。
getExternalCacheDir()
获取的是 path/Android/data/<包名>/cache
路径的 File 对象。
需要特别注意的是如果设备没有外部存储,以上外部存储访问的方法会返回 null。
当 app 删除时,这些目录以及下面的文件也会被删除。
4、注意事项
4.1 文件夹的构造
如果要在获取的路径下新建文件、文件夹等,要遵循一定的步骤。
例如某个路径为 :/data/data/<包名>/files/json
,要创建这个路径,这里如果认为 json
是一个文件夹。方法是:
-
构造路径:
String targetFolderPath = getFilesDir() + "/json";
注意
json
要带上分隔符。getFilesDir()
实际上返回的是File
对象,不过这里应当可以自动转换为路径处理,如果要求一定是字符串的话,可以采用getFilesDir().getAbsolutePath()
方法获取File
对象的绝对路径。 -
创建文件对象
File targetFolder = new File(targetFolderPath);
-
检测这个文件对象是否存在(路径是否存在,因为
getFilesDir()
一定是有这个路径的,所以就是检测这个路径下有没有名为json
的文件夹)if(!targetFolder.exists()) { // 文件夹不存在时的逻辑 // 一般是创建该文件夹 targetFolder.mkDir(); } else { //文件夹已经存在时的逻辑 // 可以给出一些提示信息等 }
4.2 文件的构造
路径实际上是文件名或者文件夹名的一种统称,可以指定特定的位置。但是文件和文件夹肯定是不同的,要建立文件,一般都要先有文件夹。还是以之前的文件夹构造为基础,如果在此基础上要建立文件,如 test.json
。
虽然文件夹和文件在 Java 中都作为 File
对象处理了,但是还是应当在变量名中区分这是文件还是文件目录,根据此调用对应的方法。
-
文件路径的构造
String targetFilePath = targetFolder + "/test.json";
这里同样是直接使用了
File
对象。 -
创建文件对象并检查是否存在
File targetFile = new File(targetFilePath); if(!targetFile.exists()) { // 文件不存在时的逻辑 // 一般是创建该文件 try { targetFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } else { //文件夹已经存在时的逻辑 // 可以给出一些提示信息等 }
这里注意要处理可能出现的
IOException
。
4.3 为什么会有 IOException
如果需要创建文件,相当于是往存储空间中进行了写操作。如果没有权限的话,Java 会抛出异常。但是 WRITE_EXTERNAL_STORAGE
和 READ_EXTERNAL_STORAGE
这两个权限,如果是对外部存储的应用私有区域操作的话是不需要的(API Level 19
及以上)。
另外注意的是 Android 设备的外部存储概念不局限于 SD 卡,对于其他可能的外设,要实现读写,同样需要申请读写权限(需要用户同意)。
4.4 总结
总结一下,创建文件的基本方式是:
构造文件夹路径 -> 构造文件夹File对象 -> 检查 File 对象是否存在,不存在则创建文件夹 -> 借助之前的 File 对象构造文件路径 -> 构造文件 File 对象 -> 检测 File 对象是否存在,不存在则创建文件,并处理 IOException
。
另外既然以上提到的存储路径都是应用私有的,用 context
获取 API 进行操作也不难理解了。
这个流程是比较模块化的,完全可以、也很适合写一个工具类来处理 文件/文件夹
的构造流程。另外一点是关于异常的处理,可以打印一些自定义的日志等。毕竟如果真的发生了 IOException
,打印 stackTrace
有些多此一举(无法避免异常)。
似乎 Kotlin 就有摒弃这种打印 stackTrace
的异常处理办法的潮流。