不知道各位有没有听过下面这种类似的段子
90后生病:我发烧38.5度,头晕得厉害,但是工作没做完,没做完就请假太对不起老板了,我得坚持下去 00后生病:劳资今天精神状态不适合上班,灵魂出窍中,请假,已读不回勿扰
而当我今天打开许久没看的Android开发者文档后,吓了一跳,谷歌又整新活儿了,Android17出了一个应用内存限制的新特性,越看越觉得Android17的手机应用里面,也出了不少“00后”
背景
总体先说下怎么回事,Android 17 引入了基于设备总RAM的应用内存限制机制。系统会"保守地设置限制",在极端内存泄漏等异常情况引发系统范围不稳定(界面卡顿、耗电过快、应用被终止)之前主动采取行动,目的是为应用和用户"打造更稳定、更确定的环境"。 个人解读:谷歌为了用户考虑,当用户使用的应用出现异常的时候,就不让用户用了,就像是app说:我有点发烧(电池过热),四肢酸痛(界面卡顿),吃坏肚子了(内存泄漏),活干不动了,系统就说:好的,你走吧,可以下班了...是这个意思吧?
影响范围
值得注意的是,这个应用内存限制的新特性是针对所有平台的,也就是不管你的应用升不升级targetSdkVersion版本,只要运行在Android17的设备上,都必须考虑这件事,但是人家也标注了,不是所有设备都会存在这个新特性

那究竟部分设备是指的哪些设备,并没有明说,不过按照以往所有新特性的规律,不排除将来对所有设备都开放的可能性
行为表现
所以对于开发者来讲,目前能做的只能是知道应用是不是因内存达到上限而被强制退出的,以及如何预防这件事情的发生,当应用突破内存限制时,系统会终止该应用进程,但是不会crash,也不会抛出OOM,仅仅只会有一个退出原因记录REASON_OTHER,然后说明中包含字符串 MemoryLimiter:AnonSwap,所以如果我们不做任何事情的话,线上如果有用户说:我的应用无故退出了,你大概率会去犟嘴:胡说,我啥日志都没看到
问题捕获
既然不会crash,那么肯定try-catch的方式也不能用,只能通过上报日志的方式知道了,前面说到了,应用被强制退出会有个原因记录,我们可以上报这个记录,这个记录是通过ApplicationExitInfo来获取的,代码如下

建议在 Application.onCreate() 或首页 Activity 中注册此检查,并上报到日志/监控系统。
使用基于触发器的生产环境分析
Android 平台提供了高级可观测性 API,支持在生产环境中基于触发器自动捕获数据:
TRIGGER_TYPE_ANOMALY | ||
TRIGGER_TYPE_OOM | OutOfMemoryError 后,下次启动时触发 |

结合 Android Vitals 监控 LMK 事件
通过 Android Vitals(Google Play Console)跟踪低内存终止 (LMK) 事件:

问题分析
当确认应用被内存限制器终止后,需要分析具体原因。
本地分析:Android Studio Memory Profiler
Memory Profiler 可以分析:
内存用量的实时图表和趋势 分配的 Java/Kotlin 对象数量 - GC 事件
发生的时间与频率 - Java 堆的快照
(Heap Dump),检查大对象与泄漏 所有分配对象的堆栈轨迹
命令行分析:dumpsys meminfo

重点关注 PSS(Proportional Set Size)——按比例分摊共享库后的实际物理内存,这是与系统限制最相关的指标。
生产环境分析:ProfilingManager
生产环境中,通过 ProfilingManager 的基于触发器的分析能力:
- TRIGGER_TYPE_ANOMALY
:系统检测到内存异常(达到限制前触发器)时自动转储堆 - TRIGGER_TYPE_OOM
:OOM 发生后下次启动时触发
查询系统可用内存

应该如何适配
知道了问题,那么接下来就要做的是我们如何去适配这个新特性,才能让应用在内存限制内运行
建立内存基线
针对不同的设备配置(RAM 大小),建立应用的内存使用基线:
- 收集各场景峰值内存
:启动、主页、列表滑动、详情页、图片浏览、后台切换 - 按 RAM 分档
:4GB、6GB、8GB、12GB+ 设备分别记录 - 设置报警阈值
:设定为系统限制的 80%,接近时触发告警
优化图片加载
图片是内存占用的最大来源之一:

实现 onTrimMemory 及时释放资源

使用经过优化的数据容器
避免 Java 标准库中带有自动装箱开销的容器:
SparseArray | HashMap<Integer, Object> | |
SparseBooleanArray | HashMap<Integer, Boolean> | |
LongSparseArray | HashMap<Long, Object> | |
ArrayList<Integer> |
精简代码与依赖

通过启用 R8 缩减应用总大小
应避免的全局规则:
-dontoptimize:完全停用整个应用的优化,导致可执行文件更大、速度更慢。 -dontshrink:防止移除未使用的代码和资源。 -dontobfuscate:防止名称缩减,从而错失宝贵的内存节省机会(尤其是在大型应用中)。 避免使用软件包级通配符:
-keep class com.example.package.** { *; }等宽泛的规则会强制 R8 保留相应软件包中的每个类、字段和方法。这会完全阻止 R8 移除、优化或缩小相应软件包中的代码使用默认 R8 配置文件:始终使用
proguard-android-optimize.txt精简 Protobuf:始终使用 protobuf-lite(精简版)而非完整版
谨慎管理 Service
启动 Service 后,系统会倾向于保持其进程存活,这将减少 LRU 缓存中的可用进程数,影响应用切换效率:

避免内存抖动
内存抖动指短时间内大量分配临时对象,导致频繁 GC,消耗电量和增加帧绘制时间:

测试与调试工具
adb memory-limiter 命令
Android 17 提供了一组新的 adb 调试工具,用于模拟和调试内存限制:

模拟低内存场景

持续监控脚本

总结
其实说白了也没啥大不了的,做的还是平时我们做的一些性能优化的事情,对于大部分Android老法师来讲,优化个内存每个人都有自己的手段,就算谷歌不弄出这个新特性,谁家kpi里面没几个性能优化的指标呢?所以,比起前面几个版本的某些特性,这个咱还真不用放心上
夜雨聆风