在开发过程中我们都会使用到手机的内部缓存、外部缓存。但有些开发者对这两个存储区域理解还够透彻,以为手机内置的存储卡(不可手机移除)就是内部存储,
可插拔的SD卡就是外部存储,其实这些理解都是有误的。这个知识点本人也重复看过好几次,但每次看完,过一段时间就会忘记,于是打算对这一知识点做个总结,也可当成学习笔记分享给大家。
主要分为下面两点进行分析:
很多同学都使用Window系统,对Window文件系统的目录结构也比较熟悉。Window使用的文件系统是NTFS,NTFS文件系统可把一个物理机械硬盘划分为四个逻辑分区,每个分区对应一个盘符,如:C盘、D盘、E盘、F盘。
分区的目的是为了便于管理文件。每个盘符在用户看来是相互独立的四块磁盘,每个盘符都是一个根节点,如下图所示:
Android系统的内核使用的是Linux内核, 所以Android的文件目录结构和Linux系统的文件目录结构类似,Linux系统和Window系统属于不同的操作系统,所以它们所使用的文件系统也是不一样的,
那对磁盘的管理方式也是不一样的,这就造成了Android设备的文件目录结构和Window系统设备的文件目录结构不一样的原因。
现在我们就来大致了解一下Android文件系统的目录结构,以及几个重要的文件目录。
Android系统使用虚拟文件系统(VFS), VFS的目录是以/为根节点,根节点下又有不同的节点。而我们的物理存储设备就是挂载都这些节点上,如下图所示:
一般来说,现在的国产手机都会有两个存储装置,一个就是内置的手机设备中不可手动拆卸的存储卡,我们称之为内置存储卡,另一个是可手动插拔外置存储卡,我们称之为外置SD卡。
对于Android开发者来说只和外部存储和内部存储打交道,Android提供的开发接口也只是获取内部存储和外部存储的目录地址。而对开发者屏蔽了内置存储卡和外置SD卡。
所以很多同学都的理解就是:内置存储卡是内部存储, 外置SD卡 是外部存储。 这样的理解是不正确的。
以下是我个人的理解:
内置存储卡 和外置SD卡 是不同物理存储装置上的划分,一个是内置到设备上,一个是在插在SD卡卡槽上。
而内部存储 和外部存储 以是否是应用的安装目录来划分,内部存储 是在应用的安装目录下,外部存储 在应用的安装目录外。
一个划分是物理上的划分,一个是逻辑上的划分,所以内置存储卡 不是内部存储 ,外置SD卡 也不是外部存储 。
注:自 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()。