Android文件存储

3,015 阅读3分钟

前言

在Android中,对于持久化有如下4种:

持久化.jpg

本篇文章主要介绍一些容易出错的地方,以及访问对应空间的API。

正文

先来看看内部存储空间。

内部存储空间

由上面图可以看出内部存储空间主要由3个部分组成,其中内部存储的目录就是/data/data/<包名>/,对应的目录如下:

内部存储空间.jpg

内部存储空间有如下特点:

  • 每个应用独占一个以包名命名的私有文件夹。
  • 在应用卸载时被删除。
  • 对MediaScanner不可见。
  • 适用于私密数据。

对于内部存储空间,里面有一些默认的文件夹,而对于不同文件的访问,有着不同的API,如下:

  1. 对于data/data/<包名>/目录:
方法描述
Context#getDir(String name,int mode): File获取内部存储根目录下的文件夹,不存在则创建
  1. 对于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删除文件或文件夹
  1. 对于data/data/<包名>/cache/目录:
方法描述
Context#getCacheDir():File返回cache文件夹
  1. 对于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卡的。

对于外部存储空间,也有严格的划分,如下:

外部存储空间划分.jpg

这里可以发现外部存储空间分为了公共目录和私有目录,对于公共目录特点:

  • 外部存储中除了私有目录外的其他空间。
  • 所有应用共享。
  • 在应用卸载时不会被卸载。
  • 对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要了解,在需要保存文件时选择适当的存储空间。