这篇文章记录的是 2019 年遇到的后台动画问题:应用界面不可见了,进程里却仍有动画工作在执行。但是当用户知道一个 App 退到后台之后还在持续做无效的动画,而这个动画完全是无意义的,而且用户还不知道他在做动画,消耗用户那可怜的电量的时候,轻则被多任务杀掉,禁止后台运行,重则直接卸载。
这个问题容易被忽略。当时我使用 Systrace ,在多开应用后退回桌面,左右滑动并抓取 Systrace ,发现部分已离开的应用仍在频繁执行动画回调。复查时应固定操作和应用版本,并记录界面可见性,不能把“打开很多应用”当成必要或充分条件。
这里讨论的是界面已不可见、业务也不再需要,却仍持续推进的动画。它会增加不必要的工作;但进程还活着不等于能无限制使用 CPU,现代系统还可能冻结 cached 进程。往 Choreographer 提交动画回调的入口很多,最终要结合具体调用栈、播放状态和库版本定位,不能仅凭一个 animation slice 判定原因
版本说明(2026-10-13 技术复核): 原案例与截图来自 2019 年,不代表网易新闻、QQ 音乐或 Lottie 当前版本的表现。下文补充 Lottie Android 6.7.1 的实现、窗口/生命周期边界和 Android 11 / API 30 起的 cached-app freezer。原始完整 Trace、应用版本和功耗测试未提供,不能把历史 CPU 图当成电量测量。
下面我们就以两个实例,从技术的角度来看一下事件发生时候的情况和原因,希望看到这篇文章的开发者,检查一下自己的应用是否有这个问题,有则改之,无则恭喜
实例 - 网易新闻
当时我用了一会儿网易新闻,返回桌面,再一边滑动桌面一边抓 Systrace。我看到应用界面已经离开前台;下面图中则能直接看到进程 com.netease.newsreader.activity(PID 30962)持续出现 doFrame。

放大选中的一帧,能看到 animation 区间,没有可见的 input/traversal 区间。这只描述该选区,不是判定应用在后台的充分条件:无输入、无 View 更新的前台帧也可能如此。图中还有与 Chrome 相关的轨道,不能把整段进程活动都算给同一个动画库。

CPU Slices 统计的选区约 3.636 s,reader.activity 这一行累计 422.163 ms、680 个运行切片,位于当前可见排序首行。这里的 680 不是动画数、回调次数或掉帧数;按名称聚合还需要继续核对线程/进程归属。CPU 运行时间也不是直接的耗电量,更不能把所有时间归到某个 animation callback。

我接着看 MethodTrace,发现 ValueAnimator 更新进入 com.airbnb.lottie...onAnimationUpdate,再推进图层和 invalidation。它把调查范围缩小到了一个 Lottie 更新路径;截图没有给出具体库版本、对象身份和可见性状态,不能据此断言所有 Lottie 版本有同一缺陷。MethodTrace 的时间也受插桩影响。

实例 - QQ音乐
接着换 QQ 音乐,我还是启动应用后返回桌面,滑动桌面并采集。历史进程 com.tencent.qqmusic(PID 19330)的轨道显示持续帧活动:

MethodTrace 中,同一段 AnimationHandler 工作里有三段 ValueAnimator/Lottie 更新调用。它提示有多个更新需要调查,但没有对象标识就不能仅凭三段栈精确断言“三个不同动画实例”;更不能据此跨应用比较耗电严重程度。

放大图中的 10.73 ms 是选中混淆方法 com.airbnb.lottie.aw.b 在这次插桩记录里的区间,不是后台功耗或可直接外推的生产环境开销。

这种问题也可能出现在普通属性动画中。下面的栈包括 ObjectAnimator、View.setRotation、invalidate,以及后面的 TraversalRunnable/performTraversals;它本身就说明“后台动画一定没有 traversal”不能作为通用规则。

根本原因
这些案例的排查方向是:动画仍在被续订、推进,而它所属界面可能已经不可见,或者业务已经不需要它。需进一步区分应用没有传递停止意图、历史库的可见性处理问题、父容器/窗口状态变化,以及回收复用或异步加载后的重新启动。
Choreographer按已登记的回调调度工作,不会因为“App 在后台”就统一删除所有 input/draw、只保留 animation。没有 traversal slice 只能说明该处没观察到相应 View 工作,不证明所有 Surface、WebView 或 native 渲染都停止,也不证明动画计算必然没有业务用途。QQ 音乐在后台播放音频是另一类功能,不能因为暂停装饰动画就停止所有后台工作。
现代系统边界: Android 11 / API 30 起支持 cached-app freezer。在支持并启用的设备上,符合条件的 cached 进程被冻结后不能继续执行 CPU 工作,直到解冻。并非每个退到后台的进程都会立即进入该状态;冻结也不能替代正确的播放状态管理。采集时要记录进程状态,避免把“进程存在”“界面不可见”和“正在运行”混为一谈。
开发建议
原文引用的是 Lottie issue #987,于 2018-10-31 提出,2019-02-03 关闭。报告者描述循环动画在 View 隐藏或 Activity paused 后仍运行,并怀疑只依赖 detach 清理不够。它是历史问题报告,关闭日期本身不证明所有版本和集成都已修复。
原来的 onVisibilityChanged() 子类补丁不宜直接照搬:它遗漏字段声明,还可能在连续两次“不可见”通知中把恢复标记从 true 清成 false;单看参数 visibility 也不能表达宿主生命周期、祖先/窗口状态和用户主动暂停。与库自身的暂停/恢复逻辑叠加后,还可能出现重复控制。
Lottie 6.7.1 已经做了什么
本次核对 6.7.1 发布版,源码固定到提交 c8addcb776f6cd940abaf55ae6b471e55b626eb9:
LottieDrawable.setVisible()会在变为不可见且 animator 正在运行时暂停,并记录后续恢复动作;重新可见时按已保存动作处理。实现专门防止连续不可见通知清掉恢复意图。LottieAnimationView使用 View/ImageView 的可见性传递,pauseAnimation()委托给 Drawable,并关闭 autoplay;不要假设当前版本还只有旧 issue 所说的 detach 清理。LottieValueAnimator.pauseAnimation()移除 Choreographer FrameCallback。Drawable 的暂停还会清理待加载后的播放任务,并在不可见时清除待恢复动作;所以“现在不在播放”不总等于“没有待执行的播放请求”。
这些是所查版本的实现,不是“升级后任何集成都自动正确”的保证。可见性传递还受系统和 target SDK 兼容路径影响,参见 Android 16 ImageView 实现。自己使用 Drawable、自定义容器、Compose 或不同版本时,应核对对应适配路径。
把播放意图与临时暂停分开
对不再可见的装饰动画,暂停通常是正确动作。但不要机械地“onPause 停一切、onResume 播一切”:Activity 在多窗口中 paused 时仍可能可见。以可见性为依据的工作通常要结合 onStart/onStop;Fragment 则跟随其 View 生命周期,销毁 View 后解除相关引用和监听。
建议由一个地方管理动画是否应该继续:
- 业务/用户是否仍请求播放,动画是否已经自然结束或主动取消。
- 宿主生命周期是否允许,以及 View、祖先、窗口、attach 状态是否满足显示需求。
isShown()也不是屏幕像素实际可见性的完整证明,不能检测一切遮挡。 - 动画资源是否已准备好;加载完成、列表复用、页面切换时重新计算,不能让过期的加载回调把动画启动回来。
下面是执行既有决策的函数示例,不是完整生命周期组件;shouldAnimate 必须由上述状态共同决定,并在状态变化时更新。单次动画完成后也应清除业务播放意图,避免随后一次状态更新把它重新播起:
1 | // Call on the main thread whenever the owning screen's playback policy changes. |
pauseAnimation() 用于暂时停在当前进度,resumeAnimation() 从当前位置继续;从头播放、自然结束和主动取消要按业务区分。不要把 isAnimating() 当成唯一的播放意图,也不要反复无条件 resumeAnimation()。调用应在主线程,并核对实际使用的 Lottie 版本及其事件语义。
如何确认修复有效
固定应用/库/系统版本,分别验证返回桌面、页面切换、父容器隐藏、窗口不可见、列表回收复用,以及后台期间资源加载完成。再返回可见状态,确认该恢复的能继续,用户已暂停或已结束的不会自动重播;多窗口和画中画按产品规则单独验证。
用同等操作和时间窗口对比目标线程的调度运行时间、动画调用栈和回调次数,检查自发续订是否停止;需要评价电量时另做受控功耗测试。没有 animation slice、没有采到线程或被冻结,都不能单独证明生命周期逻辑已修好。
本文其他地址
由于博客留言交流不方便,点赞或者交流,可以移步本文的知乎或者掘金页面
知乎 - Android 中的“后台无效动画“行为分析
掘金 - Android 中的“后台无效动画“行为分析
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
