4. 存储约束:StorageNotLow约束、存储空间检查、注意事项

4.1 先说清楚 StorageNotLow 是个啥

StorageNotLow 约束,说白了就是告诉 WorkManager:“嘿,别在设备存储快满的时候跑我的任务。” 这个约束会检查设备当前可用的存储空间是否低于某个阈值。我个人习惯叫它“存储水位线”,低于水位线的任务一律排队等着。

你想想看,后台任务如果正在写文件,突然存储爆了,轻则任务失败,重则引发应用崩溃。WorkManager 提供这个约束,就是帮你规避这种尴尬。

我在项目中遇到过好几次用户反馈“任务没执行”,查了日志发现都是存储满了,任务被约束卡住了。从那以后,只要涉及文件写入的操作,我必加这个约束。

重要:StorageNotLow 只检查设备的内置存储空间,不会检查 SD 卡或外部存储。如果任务只需要外置存储,别指望它帮你兜底。

4.2 怎么配置?代码走起

用法很简单,和设置其他约束一样。你只需要在 Constraints.Builder 里调用 setStorageNotLow(true)。

Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setStorageNotLow(true)   // 确保存储空间充足
        .build();

OneTimeWorkRequest fileWriteWork =
        new OneTimeWorkRequest.Builder(FileWriteWorker.class)
                .setConstraints(constraints)
                .build();

这里有个小细节:setStorageNotLow(true) 是开启约束,默认是 false(不检查)。如果你不显式设置,WorkManager 不会对存储做任何限制。

4.3 存储空间检查的底层逻辑

WorkManager 底层不是自己轮询,而是注册了一个 StorageNotLowTracker——它监听系统广播 ACTION_DEVICE_STORAGE_LOW 和 ACTION_DEVICE_STORAGE_OK。说白了,系统说“低了”就阻塞,系统说“好了”就放行。

那阈值是多少?Android 官方文档里没写死,不同厂商、不同 Android 版本可能不一样。通常来说,系统认为 剩余空间低于 10% 或 低于某个固定值(比如 500MB) 就算低。你要是想知道具体数值,可以看看 StorageManager.getStorageLowBytes(),但 WorkManager 直接用的系统判断。

经验:我建议你在测试时故意填满存储,看看 WorkManager 会不会真的推迟任务。避免生产环境“怎么不执行”的坑。

4.4 避坑指南:这些坑我踩过

  • 不要滥用 StorageNotLow。 如果你的任务只是内存计算,加这个约束纯属多余。任务被延迟反而影响用户体验。
  • 存储恢复后任务不一定立刻执行。 我记得有一次,存储刚刚超过阈值,系统广播发出,但 WorkManager 有内部缓冲,可能延迟几十秒才调度。
  • 与网络约束同时设置时要小心。 两个约束是“且”的关系——必须存储不低 且 网络满足条件。如果用户插着充电器但在一个信号极差的地方,任务依然不会跑。
  • 低存储状态下的重试策略要设计好。 我曾经遇到一个任务写了大量文件且加了 StorageNotLow,结果用户存储一直不足,任务被搁置了整整一天。后来我加了超时回退逻辑,如果 1 小时内存储还不足,就改用其他策略(比如优先删除缓存)。

警告:不要依赖 getStorageNotLow() 来实时判断存储状态。这个 API 只在 WorkManager 内部使用,不会暴露给开发者的运行期查询。想自己检查存储,请用 StatFs 或 StorageManager。

4.5 与其他约束搭配时的思考

StorageNotLow 常常和 RequiresBatteryNotLow 一起出现,因为用户通常会在电量低时也清理文件。我个人见过不少团队把这两个约束同时加上,结果任务几乎永远不执行。你想想,又是低电量、又是低存储——条件太苛刻了。

所以我建议:只加你认为必不可少的那一个。 比如写重要日志文件,你更关心存储够不够;而执行网络上传,你更关心电量够不够。不要无脑全 true。

下面我整理了一张对比表,帮你快速决策什么时候该加 StorageNotLow:

任务类型 是否建议加 StorageNotLow 理由
写数据库/写本地文件 强烈建议 防止写入失败导致数据损坏
下载大文件 建议 避免下载一半存储满
网络请求(纯 API) 不建议 存储不影响网络请求
压缩/清理缓存 看情况 如果清理后能释放空间,可以不加约束

核心原则:只有任务写入持久数据时才需要 StorageNotLow。单纯读取操作基本不需要。

StorageNotLow 约束工作流程 任务入队 检查所有约束(含存储) StorageNotLow 是否满足? 是 立即执行任务 否 延迟直到存储恢复 系统广播恢复后重新检查 注意:系统只发送一次 ACTION_DEVICE_STORAGE_OK,若任务错过需要等下次状态变化

最后总结一句:StorageNotLow 是个好工具,但别把它当万能药。你需要的不是“加一堆约束”,而是理解你的任务到底依赖什么。我在做大型笔记应用时,给全文索引任务加了存储约束,结果用户存储满了,索引停了一天,反而导致搜索功能不可用。后来我改成无约束 + 失败后静默重试——体验反而更好。

所以,自己多想想任务的关键路径,别被约束绑架了。