1、背景
在开发过程中我们都会使用到手机的内部缓存、外部缓存。但有些开发者对这两个存储区域理解还够透彻,以为手机内置的存储卡(不可手机移除)就是内部存储,
可插拔的SD卡就是外部存储,其实这些理解都是有误的。这个知识点本人也重复看过好几次,但每次看完,过一段时间就会忘记,于是打算对这一知识点做个总结,也可当成学习笔记分享给大家。
主要分为下面两点进行分析:
- Android设备文件系统的目录结构
- 内部存储和外部存储的区别
2、 Android文件系统目录结构
很多同学都使用Window系统,对Window文件系统的目录结构也比较熟悉。Window使用的文件系统是NTFS,NTFS文件系统可把一个物理机械硬盘划分为四个逻辑分区,每个分区对应一个盘符,如:C盘、D盘、E盘、F盘。
分区的目的是为了便于管理文件。每个盘符在用户看来是相互独立的四块磁盘,每个盘符都是一个根节点,如下图所示:
Android系统的内核使用的是Linux内核, 所以Android的文件目录结构和Linux系统的文件目录结构类似,Linux系统和Window系统属于不同的操作系统,所以它们所使用的文件系统也是不一样的,
那对磁盘的管理方式也是不一样的,这就造成了Android设备的文件目录结构和Window系统设备的文件目录结构不一样的原因。
现在我们就来大致了解一下Android文件系统的目录结构,以及几个重要的文件目录。
Android系统使用虚拟文件系统(VFS), VFS的目录是以/为根节点,根节点下又有不同的节点。而我们的物理存储设备就是挂载都这些节点上,如下图所示:
- /data/data/ apk的安装目录。 如:百度地图的安装路径是/data/data/com.baidu.com/ 注意:该目录需要获取root权限才能查看
- /system/ 存放系统应用的apk文件,即手机厂商预安装应用的apk文件 (手机厂商只需把需要预安装的apk放在该节点的相应路径下,android系统就会自己解压并安装该apk)
- /storage/ 该节点是内置存储卡和外置SD卡的挂载点,/storage/emulated/0/是内置存储卡挂载点, /storage/sdcard1是外置SD卡挂载点(不同的设备挂载节点不一样,有些设备可能会挂载到/mnt/节点)。
3、内部存储和外部存储的区别
一般来说,现在的国产手机都会有两个存储装置,一个就是内置的手机设备中不可手动拆卸的存储卡,我们称之为内置存储卡,另一个是可手动插拔外置存储卡,我们称之为外置SD卡。
对于Android开发者来说只和外部存储和内部存储打交道,Android提供的开发接口也只是获取内部存储和外部存储的目录地址。而对开发者屏蔽了内置存储卡和外置SD卡。
所以很多同学都的理解就是:内置存储卡是内部存储, 外置SD卡 是外部存储。 这样的理解是不正确的。
以下是我个人的理解:
内置存储卡 和外置SD卡 是不同物理存储装置上的划分,一个是内置到设备上,一个是在插在SD卡卡槽上。
而内部存储 和外部存储 以是否是应用的安装目录来划分,内部存储 是在应用的安装目录下,外部存储 在应用的安装目录外。
一个划分是物理上的划分,一个是逻辑上的划分,所以内置存储卡 不是内部存储 ,外置SD卡 也不是外部存储 。
- 内部存储:
/data/data/目录下都是已安装的应用程序的安装目录, 该目录下包含的文件都是以包名作为文件名的目录,如我测试demo的安装目录为:/data/data/com.bgl.storage/。
内部存储的文件是应用的私有文件,其他应用(和用户)不能访问这些文件。每个应用访问自己的内部存储是不需要权限的。 当用户卸载应用时,这些文件也会被移除。
context.getDir(String name, String mode)可创建并返回一个内部存储的文件,mode用于指示文件的创建模式,指定为MODE_PRIVATE 将会把文件设为应用的私有文件。
其他可用模式包括:MODE_APPEND、MODE_WORLD_READABLE和 MODE_WORLD_WRITEABLE。
注:自 API 级别 17 以来,常量 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 已被弃用。从 Android N 开始,
使用这些常量将会导致引发 SecurityException。这意味着,面向 Android N 和更高版本的应用无法按名称共享私有文件,
尝试共享“file://”URI 将会导致引发FileUriExposedException。 如果您的应用需要与其他应用共享私有文件,则可以将 FileProvider 与 FLAG_GRANT_READ_URI_PERMISSION 配合使用。
获取内部存储地址的方式如下:
- 外部存储:
外部存储可以是外置SD卡 ,也可以是内置存储卡 的部分分区。 外部存储可分为公共目录和私有目录
在Android4.4以前读取或写入外部存储(包括公共目录和私有目录)的文件,必须获取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系统权限。 如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
但从Android 4.4 开始,操作私有目录不再需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 权限。
因此如果我们的应用只使用到外部存储中的私有目录的话,可通过添加 maxSdkVersion 属性来声明,只需在较低版本的 Android 中请求该权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
注意:如果操作外部存储的公共目录,我们还是需要申请 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 权限。
私有目录属于应用私有,但这些私有数据是可以被其它应用访问和修改的(前提是知道应用私有目录的地址)。
当用户卸载应用时,此目录及其内容将被删除。此外,系统媒体扫描程序不会读取这些目录中的文件,因此不能从 MediaStore 内容提供程序访问这些文件。
因此,如果你正在处理的文件不适合其他应用使用(例如仅供您的应用使用的图形纹理或音效),则应该存储在外部存储上的私有存储目录。
私有目录 的获取方式如下:
注意:如果用户在计算机上装载了外部存储或移除了介质,则外部存储可能变为不可用状态。所有应用都能读取和写入放置在外部存储上的文件,并且用户可以移除这些文件。
在使用外部存储执行任何工作之前,应始终调用 getExternalStorageState() 以检查介质是否可用。介质可能已装载到计算机,处于缺失、只读或其他某种状态。
例如,以下是可用于检查可用性的几种方法:
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
有些设备支持外插SD卡,所以这类设备的外部存储由内置存储卡分区和外置SD卡组成:
在Android4.3及以下的系统,只能通过Context.getExternalFilesDir(type)来获取外部存储在内置存储卡分区上的私有目录地址,而无法获取到外部存储在外置SD卡上的地址。
从Android4.4开始,你可以通过Context.getExternalFilesDirs(type)获取一个File数组,File数组中就包含了内置存储卡分区和外置SD卡的私有目录地址。
如果您希望在支持 Android 4.3 和更低版本的同时访问两个可能的位置,请使用兼容库中的静态方法 ContextCompat.getExternalFilesDirs()。
4、总结
- Android系统使用的是虚拟文件系统(VFS),VFS提供了供存储设备挂载的节点。同一个存储设备经过分区后,不同的分区可以挂载到不同的节点上,如手机的内置存储卡。
- 使用内部存储是不需要权限的,内部存储属于应用的私有存储区域,其它应用不可访问,当应用被卸载时,内部存储中的文件也会被删除。
- 外部存储分为公共目录和私有目录,外部存储是可以全局访问的,但需要申请权限:
- Android4.4以前访问私有目录和公共目录都需要申请 WRITE_EXTERNAL_STORAGE 权限
- Android4.4以后访问私有目录不需要申请权限,但公共目录是需要申请 WRITE_EXTERNAL_STORAGE 权限
- 自 API 级别 17 以来,常量 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 已被弃用。从 Android N 开始,使用这些常量将会导致引发 SecurityException。
- 如果缓存的数据量比较大,请不要保存到内存存储中,如果想保存可共享给其它应用的数据,请保存到外部存储的公共目录中。
- 设置项的Clear Data 和Clear cache两个选项,这两个都是情况应用的缓存数据,具体区别如下:
- Clear Data清理的是外部存储中的应用私有目录下的file文件夹
- Clear Cache清理的是外部存储中的应用私有目录下的cache文件夹