Android 10 分区存储适配实战指南

1. 为什么你的App在Android 10上突然“失明”了?

如果你最近把App的targetSdkVersion升级到29(也就是Android 10),然后发现以前好好的文件读写功能突然报错,或者用户反馈说“保存的图片找不到了”,别慌,你不是一个人。这大概率就是遇到了Android 10引入的分区存储(Scoped Storage) 这个大改动。我刚开始适配的时候也踩了不少坑,比如明明申请了存储权限,却读不到自己刚保存的文档,调试了半天才发现是路径访问方式彻底变了。

简单来说,Google这次改动的核心思想是“你的地盘你做主,公共地盘守规矩”。在Android 10之前,只要应用拿到了WRITE_EXTERNAL_STORAGE权限,就相当于拿到了整个SD卡的“万能钥匙”,可以随意读写任何位置,包括其他App的私有目录。这带来了严重的隐私和安全问题,比如一个手电筒App可以偷偷扫描你的相册。分区存储就是为了解决这个问题,它把外部存储空间划分成了不同的“作用域”。

对于开发者而言,这意味着我们不能再像以前那样,简单地用File对象加上一个绝对路径(比如/sdcard/MyApp/photo.jpg)就去操作公共区域的文件了。系统现在要求我们通过更规范、更安全的API来访问文件,主要是MediaStore APIStorage Access Framework (SAF)。听起来有点复杂?别担心,这篇实战指南就是帮你把这块硬骨头啃下来。我会用最直白的语言和大量代码示例,带你一步步完成从“兼容模式”到“完全适配”的整个过程,让你彻底搞懂分区存储,写出更安全、更规范的Android应用。

2. 彻底搞懂Android的存储“地盘”:内部、外部、私有、公共

在动手适配之前,我们必须把Android的存储结构彻底理清。很多混乱都源于概念模糊。你可以把手机的存储空间想象成一个带有多层隔断和独立房间的大房子。

2.1 内部存储(Internal Storage):你的私人保险柜

这指的是/data/data/你的应用包名/这个目录。它是应用完全私有的,其他应用(没有root权限)绝对无法访问。系统会为每个应用自动创建这个目录。存放在这里的数据有两大特点:第一,应用卸载时,这个目录会被系统自动彻底清除;第二,不需要任何权限就能读写。它就像你在公司分配到的带锁的私人抽屉,只有你有钥匙。通常,我们把应用的数据库、轻量的用户偏好设置、缓存文件放在这里。

2.2 外部存储(External Storage):公司共用的文件架和你的工位

这才是分区存储改革的重灾区。外部存储又细分为两块:

  • 外部私有存储(App-specific External Storage):路径是/storage/emulated/0/Android/data/你的应用包名/。它位于SD卡(或机身内置的模拟SD卡)上,但依然是应用私有的。其他应用在默认情况下无法访问(在Android 11及更高版本中,即使有存储权限也不行)。它的生命周期和内部存储一样,应用卸载,数据清除。读写这个目录同样不需要申请存储权限。这好比是在公司公共文件区里,划给你一个带名字标签的柜子,虽然位置在公共区域,但只有你能用。
  • 外部公共存储(External Public Storage):路径就是SD卡的根目录,比如/storage/emulated/0/DCIM(相册)、/storage/emulated/0/Download(下载)等。这些目录是所有应用和用户都能看到的“公共区域”。在Android 10之前,只要应用申请了存储权限,就能在这里为所欲为。现在,分区存储就是要给这个公共区域立规矩。

为了更直观,我们看下面这个表格:

存储类型 典型路径 访问权限要求 (Android 10+) 卸载应用是否删除 类比
内部存储 /data/data/com.example.app/ 无需权限 私人保险柜
外部私有存储 /storage/emulated/0/Android/data/com.example.app/ 无需权限 公共区里带锁的个人柜
外部公共存储 /storage/emulated/0/DCIM/ 需权限,且需通过MediaStore/SAF访问 公司公共照片墙

2.3 如何正确获取这些路径?

别再硬编码路径了!不同厂商、不同Android版本的路径可能有细微差别。使用系统API才是正道:

// 获取内部存储私有文件目录
val internalFilesDir: File = context.filesDir // /data/data/包名/files
val internalCacheDir: File = context.cacheDir // /data/data/包名/cache

// 获取外部私有存储目录 (Android 4.4+)
val externalFilesDir: File? = context.getExternalFilesDir(null) // /storage/emulated/0/Android/data/包名/files
val externalCacheDir: File? = context.externalCacheDir // /storage/emulated/0/Android/data/包名/cache
// 可以指定子目录类型,系统会帮你管理
val externalPicturesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)

// 获取公共存储的根路径(谨慎使用,分区存储下直接访问受限)
val publicDownloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) // 已废弃API
// 推荐使用MediaStore API来访问公共目录

注意:Environment.getExternalStorageDirectory()(返回/storage/emul

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值