问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Android内部存储与外部存储详解

创作时间:
作者:
@小白创作中心

Android内部存储与外部存储详解

引用
CSDN
1.
https://blog.csdn.net/fuhanghang/article/details/139063231

在Android应用开发中,数据存储是一个核心话题。本文将深入探讨Android系统的内部存储和外部存储机制,帮助开发者更好地理解如何在不同场景下选择合适的存储方式。

一、存储空间概述

从存储介质来说,Android的存储空间用于数据持久化存储,属于ROM存储介质,手机关机或者退出App数据不会丢失。这里需要和经常提到的"内存"从概念上进行区分:内存属于RAM存储介质,退出App或者关机之后数据会丢失。我们在开发Android应用的过程中,避免不了要用到数据持久化技术,所谓的数据持久化就是将RAM中的临时数据永久性保存到ROM中,保证在App退出或者手机关机后数据不会丢失。

从存储结构来说,Android系统的内核使用的是Linux内核,所以Android的文件目录结构和Linux系统的文件目录结构类似。Android系统使用虚拟文件系统(VFS),VFS的目录是以"/"为根节点,根节点下又有不同的节点。例如:/data, /sytem, /mnt, /storage等等。

二、存储空间的划分

我们常用的数据持久化的方式有文件存储,数据库存储,SharedPreference存储等。在Android系统中有两个位置可以让应用实现数据持久化存储:内部存储外部存储

1、存储划分

在Android 4.4之前设备的机身存储就是内部存储,而为了弥补内部存储空间不足而插入的外置SD卡,称为外部存储。

在包含Android 4.4之后的设备中,很多中高端机器都将自己的机身存储扩展到了8G以上,将同一块存储空间从概念上分成了内部存储(internal storage)和外部存储(external storage)两部分,但其实它们都在手机内部。当然,依然可以插入SD卡来扩充存储空间,这部分的存储空间称为扩展的外部存储空间。只是现在机身存储都比较大,很少插入SD卡了。

上面两张图合并到一张图的展示:

2、内部存储

2.1 内部存储概述

前面提到过,Android系统以"/"为根节点,根节点下又有不同的节点,例如:/data, /sytem, /mnt, /storage等。内部存储在逻辑上用目录来区分的话就是/data目录下的data文件夹:/data/data,这个目录普通用户是无权访问的,用户需要手机ROOT权限才可以查看。不过开发者可以通过Android Studio的View ---- Tool Windows ---- Device File Explorer工具来查看该目录,内部存储目录的大致结构如下图所示。

2.2 内部存储 - 私有目录

2.2.1 概述

从上图可以看到,/data/data目录是按照应用的包名来组织的,每个应用在安装成功后,会自动创建新的目录(data/data/package-name),并且目录名称就是该应用的包名,所以每个应用都有专属的内部存储目录。当应用被卸载后,该目录都会被系统自动删除。所以,如果你将数据存储于内部存储中,其实就是把数据存储到自己应用包名对应的内部存储目录中。

每个应用的内部存储目录都是私有的,也就是说内部存储目录下的文件只能被宿主应用访问到,其他应用是没有权限访问的。宿主应用访问自己的内部存储目录时不需要申请任何权限。因此这部分的存储也被称为:内部存储私有目录

典型的内部存储私有目录结构如下,用户也可以根据需要自己创建新的目录:

  • app_webview:用于存储webview加载过程中的数据,如Cookie,LocalStorage等。
  • cache:用于存储使用应用过程中产生的缓存数据。
  • code_cache:存放运行时代码优化等产生的缓存。
  • databases:主要用于存储数据库类型的数据。
  • files:可以在该目录下存储文件。
  • lib:存放App依赖的so库。
  • shared_prefs:用于存储SharedPreference文件。

2.2.2 特点

  • 与宿主App的生命周期相同,应用卸载时,会被系统自动删除。
  • 宿主App可以直接访问,无需权限;
  • 其他应用无权访问;
  • 用户访问需Root权限。
  • 适合存储与应用直接相关,隐私性或敏感性高的数据。

2.2.3 API相关

// 获取的目录是/data/data/package_name,即应用内部存储的根目录
context.getDataDir();

// 获取的目录是/data/data/package_name/files,即应用内部存储的files目录
context.getFilesDir();

// 获取的目录是/data/data/package_name/cache,即应用内部存储的cache目录
context.getCacheDir();

// 获取的目录是/data/data/package_name/name,如果该目录不存在,系统会自动创建该目录。
context.getDir(String name, int mode) 

// 不同的mode
MODE_APPEND:即向文件尾写入数据
MODE_PRIVATE:即仅打开文件可写入数据
MODE_WORLD_READABLE:所有程序均可读该文件数据,Api 17废弃
MODE_WORLD_WRITABLE:即所有程序均可写入数据,Api 17废弃  

3. 外部存储

3.1 外部存储概述

通俗来说,外部存储空间就是我们打开手机系统“文件管理”后看到的内容,外部存储的最外层目录是storage文件夹,也可以是mnt文件夹,这个厂家不同也会有不同的结果。一般来说,在storage文件夹中有一个sdcard文件夹,和内部存储不同的是,外部存储根据存储特点的不同可分为三种类型:私有目录公共目录、其他目录。其中,“私有目录”属于外部存储的“私有存储空间”,“公共目录”和“其他目录”属于外部存储的“共享空间”

通常来说,应用涉及到的持久化数据分为两类:应用相关数据应用无关数据。前者是指专供宿主App使用的数据信息,比如一些应用的配置信息,数据库信息,缓存文件等。当应用被卸载,这些信息也应该被随之删除,避免存储空间产生不必要的占用,适合放到(内部存储或外部存储)“私有目录”。后者更偏向于这类信息:当应用被卸载,用户仍然希望保留于设备当中的信息。常见如,拍照类应用的图片文件,用户是使用浏览器手动下载的文件等。应用无关数据应该是宿主应用希望与其他应用共享的数据,适合存放在外部存储空间的“公共目录”或“其他目录”。

  • 私有目录:上图中的Android文件夹,这个文件夹打开之后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹,这些文件夹是应用的私有目录。
  • 公共目录:DCIM、Download、Music、Movies、Pictures、Ringtones等这种系统为我们创建的文件夹;这些目录里的文件所有应用可以分享。
  • 其他目录:除私有目录和公共目录之外的部分。比如各个App在/sdcard/目录下创建的目录,如支付宝创建的目录:alipy/,微博创建的目录:com.sina.weibo/,qq创建的目录:com.tencent.mobileqq/等。

3.2 外部存储 - 私有目录

3.2.1 特点

  • 与宿主App的生命周期相同,应用卸载时,会被系统自动删除。
  • 宿主App可以直接访问,无需权限。(备注:从4.4版本开始,宿主App可以直接读写外部存储空间中的应用私有目录,4.4版本之前,开发人员需在Manifest申请外部存储空间的文件读写权限。)
  • 其他App可以访问。(备注:自Android 7.0开始,系统对应用私有目录的访问权限进一步限制。其它App无法通过file://这种形式的Uri直接读写该目录下的文件内容,需通过FileProvider访问。)
  • 用户可直接访问,无需权限。
  • 适合存储与应用直接相关,隐私性或敏感性都不高的数据。

3.2.2 API相关

同样,Android SDK中也提供便捷的API供开发人员直接操作外部存储空间下的应用私有目录:

// 获取到的目录是/storage/emulated/0/Android/data/package_name/cache
Context.getExternalCacheDir() 

// 如果type为"",那么获取到的目录是 /storage/emulated/0/Android/data/package_name/files,  如果type为"test",那么就会创建/storage/emulated/0/Android/data/package_name/files/test目录
Context.getExternalFilesDir(String type)   

3.3 外部存储 - 公共目录

3.3.1 特点

  • 与宿主App生命周期无关,应用卸载后,数据仍然保留;
  • 所有的App都需要申请EXTERNAL_STORAGE权限,Android 6.0开始需申请动态权限;
  • 用户访问,无需权限。
  • 适合存储不敏感的数据,且希望与其他应用共享的数据。

3.3.2 API相关

// 获取到的目录是/storage/emulated/0,这个也是外部存储的根目录。
Environment.getExternalStorageDirectory() 

/* 
1.如果type为"",那么获取到的目录是外部存储的根目录即  /storage/emulated/0
2.如果type为"test",那么就在外部存储根目录下创建test目录,android官方推荐使用以下的type类型,我们在Sdcar的根目录下也经常可以看到下面的某些目录:
public static String DIRECTORY_MUSIC = "Music";
public static String DIRECTORY_PODCASTS = "Podcasts";
public static String DIRECTORY_RINGTONES = "Ringtones";
public static String DIRECTORY_ALARMS = "Alarms";
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
public static String DIRECTORY_PICTURES = "Pictures";
public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_DCIM = "DCIM";
public static String DIRECTORY_DOCUMENTS = "Documents";
*/
Environment.getExternalStoragePublicDirectory(String type)   

三、内部存储与外部存储比较

1、横向对比

2、目录结构

3、存储分类

四、总结

总的来说,如果你的应用需要存储“私密性高且与应用相关”的数据,就应该选择内部存储的私有目录,“私密性不高但与应用相关”的数据就应该选择外部存储的私有目录,两者在卸载应用后,都会被系统自动删除,如果“私密性不高且与其他应用共享,应用卸载后还希望保留”,就应该选择外部存储的公共目录或其他目录。

疑问:看了不少文章都说,因为“内部存储”空间有限或比较小,所以“内部存储”应该只存储一些占用空间较小的文件,大文件尽量选择“外部存储”空间?

笔者认为现在内部存储和外部存储都是同一块存储介质上,除非系统刻意限制,两者存储空间的上限应该只受存储介质总容量和剩余容量的限制。为了验证一下,笔者通过Adroid Studio连接手机后,在终端执行命令:adb shell df -h,来查看分区的大小。笔者手机总的ROM存储空间是256G,从下面看到 /data空间分配了226G,并且与下面的 /storage/emulated, /mnt/mdfs/sdcard的空间都是相同的226G。所以应该不存在说内部存储空间被限制到很小的情况,应该是“内部存储”和“外部存储”共享相同大小的存储空间。

可以看到剩余存储空间大小是117G,这个与我查看手机的存储信息是一致的:

为了进一步验证下,我特意使内部存储私有目录的Cache文件总用量超过1G的大小,并没有出现因为在内部存储的文件总容量过大而被系统自动清理的情况。如果你还是觉得不放心,也可以选择在“外部存储”存放数据。另外,因为“内部存储”对用户来说是不可见的(需ROOT权限),如果你希望比较方便地浏览或操作应用的某些数据,那么就应该选择将这类数据存储到“外部存储”的“私有目录”或“其他目录”。

另外,Android 10开始,在Manifest增加了新属性:android:hasFragileUserData="true",如果这里的值为“true”,在卸载App的时候,在弹出的对话框里可以由用户勾选是否保留数据。如果勾选的话,那么“外部存储 - 应用私有目录”中数据就会保留,而不会被系统清理,这也算得上是Android系统比较人性化的一面吧。但是不管这里是否勾选,内部存储私有目录里的数据都会被系统删除。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号