Android文件存储使用

Android文件存储参考 思维导图

可能遇到的问题

android系统自身自带有存储,另外也可以通过sd卡来扩充存储空间。前者好比pc中的硬盘,后者好移动硬盘。 前者空间较小,后者空间大,但后者不一定可用。 开发应用,处理本地数据存取时,可能会遇到这些问题:

  1. 需要判断sd卡是否可用: 占用过多机身内部存储,容易招致用户反感,优先将数据存放于sd卡;
  2. 应用数据存放路径,同其他应用应该保持一致,应用卸载时,清除数据: 2.1 标新立异在sd卡根目录建一个目录,招致用户反感 2.2 用户卸载应用后,残留目录或者数据在用户机器上,招致用户反感
  3. 需要判断两者的可用空间: sd卡存在时,可用空间反而小于机身内部存储,这时应该选用机身存储;
  4. 数据安全性,本应用数据不愿意被其他应用读写;
  5. 图片缓存等,不应该被扫描加入到用户相册等媒体库中去。

基本操作

  1. 使用外部存储,需要的权限,在 AndoridManifest.xml 中:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

从API 19 / Andorid 4.4 / KITKAT开始,不再需要显式声明这两个权限,除非要读写其他应用的应用数据( $appDataDir )

  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;
    }
}

存储的用量情况

  • 根据系统用户不同,所能占用的存储空间大小也有不同

在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();
        }
    }
}

路径的规律

一般地,通过 Context 和 Environment 相关的方法获取文件存取的路径。

通过这两个类可获取各种路径,如图:

    ($rootDir)
+- /data                -> Environment.getDataDirectory()
|   |
|   |   ($appDataDir)
|   +- data/com.myapp
|       |
|       |   ($filesDir)
|       +- files           -> Context.getFilesDir() /Context.getFileStreamPath("")
|       |       |
|       |       +- file1    -> Context.getFileStreamPath("file1")
|       |   ($cacheDir)
|       +- cache            -> Context.getCacheDir()
|       |
|       +- app_$name        -> Context.getDir(String name, int mode)
|
|   ($rootDir)
+- /storage/sdcard0     -> Environment.getExternalStorageDirectory()
    |                       / Environment.getExternalStoragePublicDirectory("")
    |
    +- dir1             -> Environment.getExternalStoragePublicDirectory("dir1")
    |
    |   ($appDataDir)
    +- Andorid/data/com.myapp
        |
        |   ($filesDir)
        +- files        -> Context.getExternalFilesDir("")
        |   |
        |   +- file1    -> Context.getExternalFilesDir("file1")
        |   +- Music    -> Context.getExternalFilesDir(Environment.Music);
        |   +- Picture  -> ... Environment.Picture
        |   +- ...
        |
        |   ($cacheDir)
        +- cache        -> Context.getExternalCacheDir()
        |
        +- ???

各个路径的特性

下面介绍这些路径的特性以及使用中需要注意的细节:

  1. 根目录( $rootDir ):
    • 内部存储路径: /data , 通过 Environment.getDataDirectory() 获取
    • 外部存储路径: /storage/sdcard0 (也有类似 /mnt/ 这样的),通过 Environment.getExternalStorageDirectory() 获取

示例:

	   Environment.getDataDirectory(): 
          /data

		Environment.getExternalStorageDirectory(): 
          /storage/sdcard0
  1. 应用数据目录( $appDataDir )
  • 内部储存: $appDataDir = $rootDir/data/$packageName ,
  • 外部存储: $appDataDir = $rootDir/Andorid/data/$packageName 在这些$appDataDir目录下的数据,在app卸载之后,会被系统删除,我们应将应用的数据放于这两个目录中。
  1. 外部存储中,公开的数据目录。 这些目录将不会随着应用的删除而被系统删除,请斟酌使用:
Environment.getExternalStorageDirectory(): 
     /storage/sdcard0

// 同 $rootDir
Environment.getExternalStoragePublicDirectory(""): 
     /storage/sdcard0

Environment.getExternalStoragePublicDirectory("folder1"): 
     /storage/sdcard0/folder1
  1. 应用数据目录下的目录 一般的在$appDataDir下,会有两个目录 :
    1. 数据缓存: $cacheDir = $appDataDir/cache :
    • 内部存储: Context.getCacheDir() , 机身内存不足时,文件会被删除
    • 外部存储: Context.getExternalCacheDir() 外部存储没有实时监控,当空间不足时,文件不会实时被删除,可能返回空对象

    示例:

Context.getCacheDir(): 
          /data/data/com.myapp/cache

Context.getExternalCacheDir(): 
          /storage/sdcard0/Android/data/com.myapp/cache
2. 文件目录  $filesDir = $appDataDir/files :

* 内部存储:通过 Context.getFilesDir()  获取

> Context.getFileStreamPath(String name) 返回以 name 为文件名的文件对象, name 为空,则返回  $filesDir  本身

示例:

 Context.getFilesDir(): 
          /data/data/com.myapp/files

  Context.getFileStreamPath(""):
          /data/data/com.myapp/files

  Context.getFileStreamPath("file1"):
          /data/data/com.myapp/files/file1
* 外部存储:通过 Context.getExternalFilesDir(String type) ,  type 为空字符串时获取.

	type 系统指定了几种类型:
	
	```
	  Environment.DIRECTORY_MUSIC
  		  Environment.DIRECTORY_PICTURES
  		  ...
 ```
 
 示例:
 
 ```
 Context.getExternalCacheDirs(): 
      /storage/sdcard0/Android/data/com.myapp/files

Context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)

      /storage/sdcard0/Android/data/com.myapp/files/Music
 ```     
      

3. $cacheDir / $filesDir  安全性

* 在内部存储中, $cacheDir ,  $filesDir 是app安全的,其他应用无法读取本应用的数据,而外部存储则不是。   * 在外部存储中,这两个文件夹其他应用程序也可访问。   * 在外部存储中, $filesDir 中的媒体文件,不会被当做媒体扫描出来,加到媒体库中。


4. $cacheDir / $filesDir  同级目录

* 在内部存储中:通过  Context.getDir(String name, int mode) 可获取和  $filesDir  /  $cacheDir 同级的目录

* 目录的命名规则为  app_ + name , 通过mode可控制此目录为app私有还是其他app可读写。

示例:

Context.getDir("dir1", MODE_PRIVATE):
Context.getDir: /data/data/com.srain.cube.sample/app_dir1
5. 特别注意, 对于外部存储,获取 $cacheDir  或者  $filesDir 及其下的路径

	* 在API level 8 以下,或者空间不足,相关的方法获路径为空时,需要自己构造。
@TargetApi(VERSION_CODES.FROYO)
public static File getExternalCacheDir(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)) {
        File path = context.getExternalCacheDir();

        // In some case, even the sd card is mounted,
        // getExternalCacheDir will return null
        // may be it is nearly full.
        if (path != null) {
            return path;
        }
    }

    // Before Froyo or the path is null,
    // we need to construct the external cache folder ourselves
    final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
    return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏玩转全栈

flutter接入现有的app详细介绍

接入的方式,我是参考的官方的介绍文档,我这里尝试的是android的接入方式,还算比较顺利。

81130
来自专栏技术小黑屋

Android内存泄漏检测利器:LeakCanary

到这里你就可以检测到Activity的内容泄露了。其实现原理是设置Application的ActivityLifecycleCallbacks方法监控所有Act...

22420
来自专栏码农笔录

Android全能开源项目xUtils3开发教程、简单封装

21120
来自专栏小鄧子的技术博客专栏

【译】Retrofit 2 - 如何从服务器下载文件

如果你在阅读本文前没有写过任何一行Retrofit请求代码,那么最好看一下前面几篇博客。对于很多Retrofit使用者来说:定义一个下载文件的请求与其他请求几乎...

26410
来自专栏技术小黑屋

Android处理崩溃的一些实践

对于任何程序来说,崩溃都是一件很难避免的事情,当然Android程序也不例外。在Android程序中,引起崩溃的多属于运行时异常或者错误,对于这些异常我们很难做...

19520
来自专栏水击三千

android Handler更新UI

android中经常需要更新界面某个元素的值,但是在主线程中是不可以直接更新主线程的值。这里推荐通过handler机制来更新值。 一Handler的定义: 主要...

35070
来自专栏向治洪

安全退出app,activoty栈管理

前言 由于一个同学问到我如何按照一个流程走好之后回到首页,我以前看到过4个解决方案,后来发现有做个记录和总结的必要,就写了这篇博文。(之前看小强也写过一篇,这...

350100
来自专栏7号代码

Android网络与数据存储——File存储(实现SD卡文件浏览器)

AndroidManifest.xml中manifest标签下有一个属性android:installLocation,用于指定应用程序安装在什么地方,该属性有...

33730
来自专栏知识分享

最新版小五物联知识整理(关于操作手机SD卡)

https://blog.csdn.net/yuzhiboyi/article/details/8645730

10320
来自专栏QQ音乐技术团队的专栏

[Android] Toast问题深度剖析(一)

伴随着我们开发的深入,Toast 的问题也逐渐暴露出来。本文章就将解释 Toast 这些问题产生的具体原因。

2.5K150

扫码关注云+社区

领取腾讯云代金券