在说这两个概念以前,我们需要弄懂三个名词的区别:内存、内部存储、外部存储。

内存(RAM): Random Access Memory, 也叫主存。是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质 。摘自 维基百科

内部存储(ROM): Internal Storage, 注意内部存储不是内存,如果将文件存在内部存储中,那么只能被自己的程序访问到。且一个程序所创建的文件都在和应用包名相同的目录下。 参考自 泡在网上的日子

外部存储(ROM): External Storage, 概念上有点复杂,可以酱紫区别:将手机连上电脑,能被电脑识别,就是外部存储。参考自 泡在网上的日子

接下来我们看一下安卓内部文件结构,这是机顶盒的文档结构,直接进串口看的。安卓机的话,可以看看 参考链接 中查看文档列表的方法。

标红的三个部分为我们需要关注的文件夹。接下来就进入正题。

一. 内部存储

data 目录为 内部存储 的文件夹,里面一般有两个文件夹需要我们注意:

如图所示,我们需要关注 appdata 两个文件夹。

  1. app: app 文件夹中存储的是 .apk 文件,是所有我们安装的应用(不包括系统应用)。

  2. data: data 中存储的是所有程序的包名,每个包中都会有如下几个文件夹:

    • data/data/包名/files: 存储普通文件
    • data/data/包名/databases: 存储数据库文件
    • data/data/包名/shared_prefs: 存储本地化持久数据
    • data/data/包名/cache: 存储缓存
    • data/data/包名/code_cache: 存储由运行时应用程序生成的编译或优化的代码

内部存储的特点:始终可用,不可共享

二. 外部存储

外部存储一般分为 扩展卡内存SD卡 , 一般来说,mnt (或 storage ) 为外部存储的文件夹,外部存储下一般会有 sdcard 文件夹,这个是 SD卡 的路径。而扩展卡内存,一般为 /mnt/sda/sda1 这类格式。

  1. SD卡(mnt/sdcard 或 storage/sdcard)

    • 九大公有目录,目录中的所有文件均可以被其它程序读取

      1. DCIM(Digital Camera Images),存储 相机 图片、视频文件
      2. Pictures:存储用户非相机拍摄的可用图片文件
      3. Download:存储用户下载文件的标准目录
      4. Movies:存储可用电影文件
      5. Alarms:存储闹钟音频文件
      6. Notifications:存储通知列表中的音频文件
      7. Ringtones:存储铃声列表中的音频文件
      8. Podcasts:存储播客列表中的音频文件
      9. Music:存储其它音频文件(音乐等…)
    • 私有目录

      Android/data 即为私有目录。data 下放着一系列包名组成的文件,与内部存储的 data/data 有点像。每个包名代表对应应用程序的私有目录。把文件放在这里,会减轻内部存储的压力,但是会将数据暴露给用户和其它程序。

  2. 扩展卡内存

    扩展卡内存 即用户插入的外置 SD卡,没有直接的方法可以获取到扩展卡的路径。获取扩展卡内存的路径可以参考 这里

外部存储的特点: 并非一直可用、全局可读

三. 总结内 / 外存储目录

  1. 内部存储目录: /data
  2. 外部存储目录:
    • 内置 SD 卡:/mnt/sdcard 或者 /storage/sdcard
    • 扩展 SD 卡: /mnt/sda/ /mnt/sdb 或 /storage/sda /storage/sdb

四. 操作存储空间

Google 官方建议 app 的数据应该存储在外部存储的私有目录 /mnt/sdcard/Android/data/ 或 /storage/sdcard/Android/data 下。这样当用户卸载 app 时,会将相关的数据一并删除。

我们可以将文件目录按 是否拥有包名 来进行分类:

  1. 拥有包名的路径

    <tr>
        <td rowspan="5" align="center"><b>内部存储<br> /data/data/包名/</b></td>
        <td>files</td>
        <td>context.getFilesDir()</td>
    </tr>
    <tr>
        <td>cache</td>
        <td>context.getCacheDir()</td>
    </tr>
    <tr>
        <td>shared_prefs</td>
    </tr>
    <tr>
        <td>code_cache</td>
        <td>context.getCodeCacheDir() (API level 21)</td>
    </tr>
     <tr>
        <td>databases</td>
        <td>context.getDataDir() (API level 24)</td>
    </tr>
    <tr>
        <td rowspan="2" align="center"><b>外部存储(mnt 或 storage)<br> /mnt/sdcard/ 或 /storage/sdcard/</b></td>
        <td>Android/data/包名/files</td>
        <td>context.getExternalFiles("文件夹名")</td>
    </tr>
    <tr>
        <td>Android/data/包名/cache</td>
        <td>context.getExternalCacheDir()</td>
    </tr>
    

可以看到,所有拥有包名路径的基本都可以使用 context 来获取到对应文件

下面我们用代码来看一下

1
2
3
4
5
6
7
8
9
10
File internalFiles = mContext.getFilesDir();
File internalCache = mContext.getCacheDir();

Log.i(TAG, "onCreate: internalFiles----" + internalFiles.toString());
Log.i(TAG, "onCreate: internalCache----" + internalCache.toString());

File externalCache = mContext.getExternalCacheDir();
File externalMusic = mContext.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
Log.i(TAG, "onCreate: externalCache----" + externalCache);
Log.i(TAG, "onCreate: externalMusic----" + externalMusic);

打印结果

  1. 不拥有包名的路径

    可以使用 Environment 来获取到对应路径。Environment 有以下方法获取到指定目录:

    1
    2
    3
    4
    5
    6
    7
    public static File getDataDirectory();
    public static File getExternalStorageDirectory();
    public static File getDownloadCacheDirectory();
    public static File getRootDirectory();
    // 获取九大公有目录
    // 例:File music = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
    public static File Environment.getExternalStoragePublicDirectory(String type);

    下面我们来看一下使用上述方法后的打印结果(具体代码就不贴了)

五. 参考文档

  1. Android内、外存储 易混淆点剖析
  2. Android存储路径你了解多少?
  3. 彻底理解android中的内部存储与外部存储
  4. 数据存储-文件存储
  5. android中的文件操作详解以及内部存储和外部存储

六. 最后

整篇文章基于 Android API 19。对于 Android 6.0 后的多用户特性,内部存储通过同样的方法,会拿到类似于 /data/user/0/包名/xxx 之类的路径,这个在以后的文章中再做说明(主要是现在还没有了解到啦~)。