Android文件存储
前言
在Android中,对于持久化有如下4种:
本篇文章主要介绍一些容易出错的地方,以及访问对应空间的API。
正文
先来看看内部存储空间。
内部存储空间
由上面图可以看出内部存储空间主要由3个部分组成,其中内部存储的目录就是/data/data/<包名>/
,对应的目录如下:
内部存储空间有如下特点:
- 每个应用独占一个以包名命名的私有文件夹。
- 在应用卸载时被删除。
- 对MediaScanner不可见。
- 适用于私密数据。
对于内部存储空间,里面有一些默认的文件夹,而对于不同文件的访问,有着不同的API,如下:
- 对于
data/data/<包名>/
目录:
方法 | 描述 |
---|---|
Context#getDir(String name,int mode): File | 获取内部存储根目录下的文件夹,不存在则创建 |
- 对于
data/data/<包名>/files/
目录:
方法 | 描述 |
---|---|
Context#getFilesDir():File! | 返回files文件夹 |
Context#fileList(): Array! | 列举files目录下所有文件和文件夹,String类型为文件或者文件夹的名字 |
Context#openFileInput(String name):FileInputStream | 打开files文件下的某个文件的输入流,不存在则抛出异常:FileNotFoundException |
Context#openFileOut(String name,int mode):FileOutputStream | 打开files文件下的某个文件的输入流,文件不存在则新建 |
Context#deleteFile(String name): Boolean | 删除文件或文件夹 |
- 对于
data/data/<包名>/cache/
目录:
方法 | 描述 |
---|---|
Context#getCacheDir():File | 返回cache文件夹 |
- 对于
data/data/<包名>/code_cache
目录:
方法 | 描述 |
---|---|
Context#getCodeCacheDir():File | 返回优化过的代码目录,如JIT优化 |
上述方法测试代码如下:
val testDir = getDir("rootDir", MODE_PRIVATE)
//打印为:/data/user/0/com.wayeal.ocr/app_rootDir
Logger.t("testFile").d("testDir = ${testDir.absolutePath}")
//打印为:/data/user/0/com.wayeal.ocr/files
Logger.t("testFile").d("filesDir = ${filesDir.absolutePath}")
//在files目录下新建文件
val fileOutputStream = openFileOutput("filesTest", MODE_PRIVATE)
//打印为:[datastore, bugly_last_us_up_tm, local_crash_lock, filesTest]
Logger.t("testFile").d("fileList = ${fileList().toMutableList()}")
File(filesDir,"haha").createNewFile()
//打印为:[datastore, bugly_last_us_up_tm, haha, filesTest]
Logger.t("testFile").d("fileList = ${fileList().toMutableList()}")
外部存储空间
对于外部存储空间在使用前一般要判断是否挂载,因为早期的的Android手机是有SD卡的,是可以进行卸载SD卡的。
对于外部存储空间,也有严格的划分,如下:
这里可以发现外部存储空间分为了公共目录和私有目录,对于公共目录特点:
- 外部存储中除了私有目录外的其他空间。
- 所有应用共享。
- 在应用卸载时不会被卸载。
- 对MediaScanner可见。
- 适用于非私密数据,不需要随应用卸载删除。
对于私有目录,有如下特点:
- 目录名为Android。
- 在media和data等目录中,以包名区分各个应用。
- 在应用卸载时被删除。
- 对MediaScanner不可见。(对多媒体文件夹例外,要求API 21)
- 适用于非私密数据,需要在应用卸载时删除。
这里对于公共目录storage/emulated/0/
来说,其API主要是Environment类来完成,如下:
方法 | 描述 |
---|---|
Environment.getExternalStorageDirectory(): File | 获取外部存储目录 |
Environment.getExternalStoragePublicDirectory(name: String): File | 外部存储根目录下的某个文件夹 |
Environment.getExternalStorageState(): String | 外部存储的状态 |
对于外部空间的私有目录storage/emulated/0/Android/data/<包名>/
来说,其API还是由Context,主要是方法名都携带external字样,如下:
方法 | 描述 |
---|---|
Context.getExternalCacheDir(): File | 获取cache文件夹 |
Context.getExternalCacheDirs(): Array | 多部分cache文件夹(API 18),因为外部存储空间可能有多个 |
Context.getExternalFilesDir(type: String): File | 获取files文件夹 |
Context.getExternalFilesDirs(type: String): Array | 获取多部分的files文件夹 |
Context.getExternalMediaDirs(): Array | 获取多部分多媒体文件(API 21) |
上述方法测试代码和log如下:
Logger.t("testFile")
.d("外部公共存储根目录 = ${Environment.getExternalStorageDirectory().absolutePath}")
//外部公共存储根目录 = /storage/emulated/0
Logger.t("testFile")
.d("外部公共存储Pictures目录 = ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath}")
//外部公共存储Pictures目录 = /storage/emulated/0/Pictures
Logger.t("testFile")
.d("外部公共存储状态 = ${Environment.getExternalStorageState()}")
//外部公共存储状态 = mounted
Logger.t("testFile")
.d("外部存储私有缓存目录 = ${externalCacheDir?.absolutePath}")
//外部存储私有缓存目录 = /storage/emulated/0/Android/data/com.wayeal.ocr/cache
Logger.t("testFile")
.d("外部存储私有多部分缓存目录 = ${externalCacheDirs?.toMutableList()}")
//外部存储私有多部分缓存目录 = [/storage/emulated/0/Android/data/com.wayeal.ocr/cache]
Logger.t("testFile")
.d("外部存储私有files的Pictures目录 = ${getExternalFilesDir(Environment.DIRECTORY_PICTURES)}")
//外部存储私有files的Pictures目录 = /storage/emulated/0/Android/data/com.wayeal.ocr/files/Pictures
Logger.t("testFile")
.d("外部存储私有媒体多部分目录 = ${externalMediaDirs.toMutableList()}")
//外部存储私有媒体目录 = [/storage/emulated/0/Android/media/com.wayeal.ocr]
总结
对于不同的存储空间的特点以及API要了解,在需要保存文件时选择适当的存储空间。
作者:yuanhao
来源:juejin.cn/post/7158365077488271367
来源:juejin.cn/post/7158365077488271367