参考文章:
https://blog.csdn.net/baidu_36385172/article/details/79695308
https://www.jianshu.com/p/a39bc4b3a1a6
内部存储&外部存储
Android系统目录结构
Android系统使用虚拟文件系统(VFS),VFS提供了供存储设备挂载的节点。同一个存储设备经过分区后,不同的分区可以挂载到不同的节点上,如手机的内置存储卡。VFS的目录是以/为根节点,根节点下又有不同的节点。而我们的物理存储设备就是挂载都这些节点上。
逻辑划分&物理划分
在Android4.4之前,内部存储和外部存储跟实际的物理存储是等同的,内部存储即为手机自身的物理存储空间,外部存储为SD卡的存储空间。
在Android4.4之后,随着外置SD卡的使用越来越少,内部存储和外部存储和实际物理介质存储开始分开。内部存储和外部存储是逻辑划分的概念,不是按照物理磁盘划分的。
从下面的图可以看出,内部存储只位于手机自身的物理存储空间中,外部存储既可以位于手机自身的物理存储空间,也可以在外置的SD卡中。
内部存储和外部存储和物理存储的关系图
分区存储
为了更好的管理应用文件减少应用文件混乱,并加强用户数据隐私保护,从 Android 10(API 级别 29)开始引入了分区存储的机制。分区存储机制中,外部存储空间被重新设计,外部存储空间按应用专属目录和共享目录进行区分,应用只能访问到外部存储空间上的应用专属目录,和本应用所创建的特定类型的媒体文件。
内部存储上的应用专属目录: 只是包含/data/data/packagename/ 目录;
外部存储空间上的应用专属目录: 存储应用私有数据,外部存储空间上的应用专属目录为:/storage/emulated/0/Android/data/packagename/ (/storage/emulated/0/目录根据机型不同可能会有所不同)。
注意内部存储空间上的应用专属目录和外部存储应用专属目录是不同的;
外部存储空间上的共享目录: 存储其他应用可访问文件, 包含媒体文件、文档文件以及其他文件,对应设备DCIM/,Movies/,Pictures/,Documents/, Download, Music/, Movies/ 等目录;
使用单测查看Android系统上的外部存储目录路径:
import android.content.Context;
import androidx.core.content.ContextCompat;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import java.io.File;
import java.nio.charset.StandardCharsets;
/**
* @Author: wangzhongxing
* @CreateDate: 2022/5/18 11:05
* @Description:
*/
public class FileUtilsTest {
@Test
public void getAndroidExternalStoragePathTest() {
Context context = ApplicationProvider.getApplicationContext();
File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(context, null);
String externalStorageVolumePath1 = externalStorageVolumes[0].getPath();
String externalStorageVolumePath2 = null;
if (externalStorageVolumes.length >1) {
externalStorageVolumePath2 = externalStorageVolumes[1].getPath();
}
System.out.println("ContextCompat.getExternalFilesDirs()[0]=" + externalStorageVolumePath1);
System.out.println("ContextCompat.getExternalFilesDirs()[1]=" + externalStorageVolumePath2);
File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
String externalCachePath1 = externalCacheDirs[0].getPath();
String externalCachePath2 = null;
if (externalCacheDirs.length >1) {
externalCachePath2 = externalCacheDirs[1].getPath();
}
System.out.println("ContextCompat.getExternalCacheDirs()[0]=" + externalCachePath1);
System.out.println("ContextCompat.getExternalCacheDirs()[1]=" + externalCachePath2);
File[] externalMediaDirs = context.getExternalMediaDirs();
String externalMediaPath1 = externalMediaDirs[0].getPath();
String externalMediaPath2 = null;
if (externalMediaDirs.length > 1) {
externalMediaPath2 = externalMediaDirs[1].getPath();
}
System.out.println("context.getExternalMediaDirs()[0]=" + externalMediaPath1);
System.out.println("context.getExternalMediaDirs()[1]=" + externalMediaPath2);
String dataPath = ContextCompat.getDataDir(context).getPath();
System.out.println("context.getDataDir().getPath()=" + dataPath);
File[] obbDirs = ContextCompat.getObbDirs(context);
String obbPath1 = obbDirs[0].getPath();
String obbPath2 = null;
if (obbDirs.length > 1) {
obbPath2 = obbDirs[1].getPath();
}
System.out.println("ContextCompat.getObbDirs()[0]=" + obbPath1);
System.out.println("ContextCompat.getObbDirs()[1]=" + obbPath2);
}
}
输出结果:
I/TestRunner: started: getAndroidExternalStoragePathTest(com.insigma.traffic.acquisition.FileUtilsTest)
I/System.out: ContextCompat.getExternalFilesDirs()[0]=/storage/emulated/0/Android/data/com.insigma.traffic.acquisition/files
I/System.out: ContextCompat.getExternalFilesDirs()[1]=/storage/0B06-2D0D/Android/data/com.insigma.traffic.acquisition/files
I/System.out: ContextCompat.getExternalCacheDirs()[0]=/storage/emulated/0/Android/data/com.insigma.traffic.acquisition/cache
I/System.out: ContextCompat.getExternalCacheDirs()[1]=/storage/0B06-2D0D/Android/data/com.insigma.traffic.acquisition/cache
I/System.out: context.getExternalMediaDirs()[0]=/storage/emulated/0/Android/media/com.insigma.traffic.acquisition
I/System.out: context.getExternalMediaDirs()[1]=/storage/0B06-2D0D/Android/media/com.insigma.traffic.acquisition
I/System.out: context.getDataDir().getPath()=/data/user/0/com.insigma.traffic.acquisition
I/System.out: ContextCompat.getObbDirs()[0]=/storage/emulated/0/Android/obb/com.insigma.traffic.acquisition
I/System.out: ContextCompat.getObbDirs()[1]=/storage/0B06-2D0D/Android/obb/com.insigma.traffic.acquisition
系统文件目录
内部存储和外部存储和系统上的文件路径关系图
内部存储和外部存储权限关系图
按照目录是应用专属的还是不同应用共用的,可分成应用专属空间和共享存储空间。
应用专属空间
应用专属目录可以位于内部存储空间,也可以位于外部存储空间。应用专属目录下的数据只能本应用自己访问,不能跨应用访问。
内部存储空间上的应用专属目录: /data/data/packagename/,
该目录下还可以详细划分为:files/ 和 cache/ ,files/ 用于存放持久性文件,cache/ 用于存放缓存文件。
外部存储空间上的应用专属目录: /storage/emulated/0/Android/data/packagename/ (/storage/emulated/0/ 目录根据机型不同可能会有所差异)
该目录下同样可以详细划分为:files/ 和 cache/ ,files/ 用于存放持久性文件,cache/ 用于存放缓存文件。
共享存储空间