存储区​

Android 一开始就将存储区分为
内部存储

外部存储
,对应手机自带的存储和可插拔的 sd 卡(可类比于 PC 的硬盘和 U盘)。

内部存储容量有限,Google 建议 App 数据尽量存储于外部存储中。

随着硬件技术发展,自带大容量空间的手机开始出现,关于内部存储的描述逐渐偏离现实了,于是从
Android 4.4(API 19)
开始,官方不再将机身存储等同于内部存储,而是从逻辑上将其一部分划到外部存储,限制剩下那部分的容量,也就是现在所谓的内部存储。这一操作,使得原本内部存储和外部存储的特性和使用场景得以延续。

当然,如果在 4.4 系统及以上的手机上插了 sd 卡,那么 sd 卡也属于外部存储。

我们可以使用如下代码打印出所有的外部存储:

File[] files;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
    for(File file:files){
        Log.e("main",file);
    }
}

对于 4.4 以上的插了 sd 卡的大容量手机,应该会打印出如下信息:

/storage/emulated/0/Android/data/packname/files/mounted 
/storage/B3E4-1711/Android/data/packname/files/mounted 

文件

应用专属文件

仅供应用使用的文件,可以存储到内部存储或外部存储中的本应用专属目录,本应用访问时不需要任何权限。在数据安全方面,虽然都是专属目录,但是内部存储可以保证其它应用访问不到,而外部存储就比较复杂了。

在较低版本的 Android 系统中,只要声明
READ_EXTERNAL_STORAGE
权限就能访问位于外部存储空间中应用专属目录之外的任何文件;只要声明
WRITE_EXTERNAL_STORAGE
权限就能向应用专属目录以外的任何文件写入数据。

这实在是相当危险,谁也不希望自家应用中的数据被抓取或篡改。于是从
Android 10(API 29)
开始有了
分区存储
的概念,应用在默认情况下就能访问外部存储空间上自己的专属目录,以及本应用所创建的特定类型的媒体文件(使用
MediaStore API
,下面会讲到)。如此,除非特殊情况,应用不再需要声明上述权限了。

此时,如果应用在运行时请求与存储相关的权限,将会弹出请求对话框(动态申请)表明应用正在请求对外部存储空间的广泛访问权限。

Android 11(API 30)
开始更进一步,干脆将 WRITE_EXTERNAL_STORAGE 权限的作用抹除(即使声明了该权限也没用)。这将应用的写权限完全限制在了本应用相关目录(专属目录和本应用创建的媒体文件)中。

ps:Android 11 引入了
_
MANAGE_EXTERNAL_STORAGE
_
权限,该权限替代 WRITE_EXTERNAL_STORAGE,提供对应用专属目录和 MediaStore 之外文件的写入权限,但对使用的要求更严格。如需了解详情,请参阅有
管理存储设备上所有文件

共享文件

存储您的应用打算与其它应用共享的文件,包括媒体(图片、音频文件、视频)、其它类型文件。

媒体文件

使用 MediaStore API 访问。注意:即使您的应用已卸载,作为共享文件(保存在媒体库中)的媒体文件仍会保留在用户的设备上。

除访问自己的媒体文件外,访问其它应用的媒体文件需要权限——在 Android 11(API 30)或更高版本中,需要 READ_EXTERNAL_STORAGE;在 Android 10(API 29)中,需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE;在更低版本中,访问所有文件均需要相关权限。——不过这也不是绝对的。

比如
照片选择器
,它提供了一个可浏览界面,为用户提供了一种安全的
内置授权
方式,让用户可以向应用授予限于所选图片和视频的访问权限,而非整个媒体库的访问权限,该权限保留至设备重启或应用停止运行。使用照片选择器可以看作定制的动态申请权限的界面,至少从
Android 13(API 32)
开始,无需事先声明 READ_EXTERNAL_STORAGE。

其它文件

自 Android 4.4(API 19)始,官方提供了
存储访问框架
,便于应用与外部存储卷和云端存储空间在内的文档提供器互动。此框架支持用户与系统选择器互动,从而选择文档提供器以及供您的应用创建、打开或修改的特定文档和其它文件。

同照片选择器类似,由于用户参与选择您的应用可以访问的文件或目录,因此该机制无需任何系统权限,同时用户控制和隐私保护也得到了增强。

这些文件存储在应用专属目录和媒体库之外,且在应用卸载后仍会保留在设备上。

使用存储访问框架涉及以下步骤:

  1. 应用调用包含存储相关操作的 intent(
    ACTION_CREATE_DOCUMENT
    保存文件;
    ACTION_OPEN_DOCUMENT
    打开文件;
    ACTION_OPEN_DOCUMENT_TREE
    授予应用对该目录中所有文件和子目录的访问权限)。
  2. 用户看到一个系统选择器,供其浏览文档提供器并选择将执行存储相关操作的位置或文档。
  3. 应用获得对代表用户所选位置或文档的 URI 的读写访问权限。利用该 URI,应用可以在选择的位置执行操作。

数据

应用配置项

不赘述,就是简单的键值对。值得一提的是,之前都是使用
SharedPreferences
进行应用配置项的操作,现在官方建议使用
Jetpack DataStore
,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 基于
Kotlin 协程

Kotlin.Flow
以异步、一致的事务方式存储数据。

数据库

基于
SQLite
的数据存储,一般选择
Jetpack.Room
这个半 ORM 简化数据 CRUD 操作。卸载应用时数据库会跟着删除。

标签: none

添加新评论