Skip to main content

Chapter 28: Process, Thread, Scheduler, Foreground Responsiveness

本章讨论一个手机上每天都会出现的问题:用户触摸屏幕之后,为什么有的应用能立刻响应,有的应用会卡顿、掉帧,甚至出现 ANR 或类似的无响应现象。读完本章,读者应能把一次前台交互拆成进程、线程、调度器、CPU 拓扑和系统策略之间的责任链,并能判断卡顿责任落在 App 主线程、系统服务等待、CPU 调度延迟、后台降级、热限制还是资源竞争上。

贯穿本章的材料是一条具体路径:用户在信息流页面快速滑动,列表一边处理触摸事件,一边刷新可见单元格,同时有图片解码、网络回调、数据库读取和日志上报在后台执行。这个场景覆盖移动 OS 调度最容易混淆的几层:用户看到的是 UI 延迟,App 写到的是线程和队列,系统管理的是进程状态和调度组,内核最终分配的是 CPU 执行时间,硬件侧还受到大小核、温度和电池电流约束。

前台响应性的核心结论是:手机系统优先保证用户正在等待的那条执行链尽快拿到 CPU,并把后台、缓存和长耗时工作压到更低的调度位置。这个优先级并非只由 App 自己设置。Android 会把进程生命周期、组件状态、cgroup / task profile 和系统服务策略叠加到 Linux 调度之上;Apple 平台会把主线程、QoS、XPC 服务、后台模式和 thermal state 叠加到 XNU 调度之上。平台差异很大,共同问题稳定:谁在前台,谁阻塞了交互线程,谁占用了可运行队列,谁触发了降级。

下面的图只描述本章的分析边界:从一次输入事件进入 App 到用户看到下一帧之间,哪些执行单元会竞争 CPU,哪些系统层会改变竞争结果。

这条路径里的每个节点都可能制造延迟,但它们的诊断证据不同。主线程阻塞通常表现为输入处理和绘制回调滞后;worker 线程过多通常表现为同一进程内 runnable 线程堆积;Binder 或 XPC 等待通常表现为 App 线程在等系统服务返回;调度组降级通常表现为后台状态下 CPU 时间减少;thermal throttle 通常表现为同样任务在发热后耗时变长。后续小节会把这些现象逐层放回系统边界。

28.1 Process 作为 App 与 System Service 的执行容器

进程是移动 OS 用来承载代码执行、资源归属和安全边界的基本容器。一个 App 进程通常包含应用代码、运行时、堆内存、线程集合、文件描述符或端口引用,以及系统分配给这个进程的身份信息。系统服务进程也使用同样的内核进程模型承载代码,只是它们通常拥有更高权限、更稳定生命周期和更多硬件能力代理职责。

在 Android 中,多数普通应用在独立 Linux 进程中运行,系统会根据 Activity、Service、BroadcastReceiver、ContentProvider 等组件状态决定进程重要性。Android process lifecycle 文档把前台、可见、服务、缓存等状态与进程保留策略关联起来;这些状态随后会影响进程被杀、被降级、被限制执行时间的概率。system_server、native daemon、HAL 进程和 vendor service 同样是进程,只是它们承载系统服务或厂商能力,位于 App 请求路径的中间层。

Apple 平台也把 App、extension、XPC service、daemon 放在进程边界内运行。公开文档能确认的部分包括 App 主进程、extension 的受控执行模型、XPC 作为进程间通信模型、QoS 影响任务调度等。daemon 与部分系统服务的内部实现属于私有系统边界,正文只能使用公开行为和公开接口推断它们在路径中的角色:它们接收 framework 请求,执行权限或 entitlement 派生的策略,最后把结果返回 App 或系统 UI。

在本章的信息流滑动场景中,App 进程是列表 UI、图片缓存、网络回调和数据库读取的共同容器。图片解码线程、网络回调线程和数据库线程即使位于同一进程,也会和 UI thread 争夺 CPU。系统服务进程则承载输入分发、窗口管理、图形合成、网络状态或存储访问的部分责任。一次卡顿可能发生在 App 进程内部,也可能发生在 App 等待系统服务返回时。

把进程作为执行容器分析时,先看三类证据。第一类是进程状态:前台、可见、服务、后台、缓存。第二类是进程内线程状态:哪些线程 runnable,哪些线程 blocked,哪些线程持有锁。第三类是跨进程等待:App 是否正在等待 Binder、XPC、socket、文件 I/O 或图形合成信号。只看 App 函数耗时会漏掉系统服务等待;只看 CPU 占用会漏掉锁等待和 IPC 等待。

进程边界还决定故障后果。App 进程崩溃通常只影响该 App;系统服务进程卡住会影响多个 App 的能力请求;daemon 或 driver 相关进程异常可能影响相机、音频、网络、输入或显示链路。前台响应性分析从进程开始,是因为用户看到的一次卡顿最终要落回某个执行容器和它持有的资源状态。

28.2 Thread、Kernel Thread 与用户态线程模型

线程是调度器直接分配 CPU 的执行单元。进程负责资源归属,线程负责具体执行。一个 App 进程可以有 UI thread、render thread、worker thread、GC thread、network callback thread、database thread,也可以接收 Binder 或 XPC 回调。内核还运行 kernel thread 处理驱动、回收、写回、调度辅助和电源管理等工作。

UI thread 是前台响应链的中心。它负责接收输入回调、更新界面状态、触发布局和绘制请求,并把下一帧需要的工作交给渲染或合成链路。Android 的 ANR 文档明确把 UI thread 长时间阻塞和用户输入无法处理、无法绘制联系起来,并列出输入分发超时等 ANR 条件;其中输入事件 5 秒未响应会触发 Input dispatching timed out 类型的 ANR,Android ANR 文档对此有明确说明。这个数字用于理解 Android 用户可见无响应的上层阈值,底层掉帧通常在更短时间内已经发生。

Render thread 或图形相关线程承担另一类响应压力。现代移动 UI 常把部分渲染准备、动画、纹理提交或合成协作从主线程拆出去。拆分能减少主线程负担,但调度压力会转移到多个线程之间的协作:UI thread 准备状态,render thread 准备帧,系统 compositor 接收 layer 或 buffer,display pipeline 等待下一次刷新窗口。任何一个环节错过 frame deadline,用户都可能看到 jank。

Worker thread 解决耗时任务与交互线程隔离的问题。图片解码、JSON 解析、数据库查询、网络响应处理、日志压缩都适合放到 worker。这个设计仍然受 CPU 预算约束:worker 数量过多会让 runnable queue 变长,调度器必须在更多线程之间切换;worker 持有 UI thread 需要的锁会制造优先级反转;worker 向主线程同步等待结果会把后台耗时重新推回前台路径。

Binder / XPC callback 线程处在 App 和系统服务之间。Android 的 Binder 线程池负责接收跨进程调用回调;Apple 平台的 XPC 服务和 dispatch queue 也会把跨进程结果投递到某个执行上下文。回调线程本身属于用户态线程,但它的可运行状态依赖另一端进程、内核 IPC 对象和系统服务队列。信息流滑动时,如果主线程同步等待图片权限、存储查询或配置服务返回,前台响应就被跨进程路径绑定。

Kernel thread 与 interrupt context 属于内核执行路径。它们一般不承载 App 代码,却会消耗 CPU 时间并影响 App 可运行线程的延迟。网络包到达、触摸中断、文件写回、压缩内存、热管理和驱动回调都会让内核侧工作进入 CPU 竞争。移动 OS 诊断卡顿时,需要把“App 线程正在 runnable 但迟迟未运行”和“App 线程自身 blocked”区分开;前者指向调度压力或 CPU 竞争,后者指向锁、IPC、I/O 或等待事件。

本节的可复用判断是:先找用户等待的线程,再找它等待谁。用户等待 UI thread,就检查主线程是否在执行、等待、被抢占或被锁阻塞;用户等待后台结果,就检查 worker 与 IPC 回调是否及时完成;系统整体变慢,就检查 kernel thread、驱动事件和热限制是否改变了可用 CPU。

28.3 Scheduler 与 CPU 时间片分配

调度器负责在可运行线程之间选择下一个获得 CPU 的线程。移动 OS 的调度结果来自三层信息叠加:内核调度类和优先级决定基本选择规则,平台进程状态和调度组改变线程权重或可用 CPU 集合,电源和温控策略改变可用频率、核心选择和迁移成本。

在 Linux 语境中,普通任务长期使用 fair scheduler 一类的策略组织 runnable task。Linux 文档中的 CFS 设计把普通任务放到按虚拟运行时间排序的结构中,并以尽量公平分配 CPU 为目标;同时文档也说明现代内核调度实现会演进,例如 CFS 正在给 EEVDF 让出空间。Linux CFS 文档适合用来理解 runnable queue、虚拟运行时间、preemption、group scheduler 等基本概念,具体手机内核还会叠加厂商调度策略和版本差异。

Android 在 Linux 调度之上加入面向移动平台的分组策略。AOSP 文档说明,Android 10 及以上使用 cgroup abstraction layer 和 task profiles,把线程或进程加入特定 cgroup,并通过 task profile 描述限制或调度属性;Android 11 及以上提供 SetTaskProfilesSetProcessProfiles 这类私有 API,AOSP cgroup abstraction layer 文档描述了这条抽象层。对 App 开发者来说,这些细节通常不可直接控制,但它们会影响前台进程、后台服务、top-app、system server 和缓存进程之间的 CPU 竞争。

Apple 平台使用 QoS 把任务重要性传给系统。Apple 的 Energy Efficiency Guide 说明,QoS 会影响 scheduling、CPU and I/O throughput、timer latency 等系统决策,并把 user-interactive、user-initiated、utility、background 作为主要类别;同一文档还说明 iOS 8 及以上支持 QoS,Apple QoS guide可作为公开边界。公开材料无法直接展开 XNU 内部每个版本的调度实现,本章使用 QoS 作为 Apple 平台上层向内核传递工作重要性的稳定入口。

CPU 时间片分配不等于固定轮流。一个 runnable 线程能否很快运行,取决于它所在调度类、优先级或 QoS、进程状态、cgroup 权重、CPU affinity、当前 CPU 是否忙、是否有高优先级线程唤醒、是否发生抢占、系统是否处于热限制。信息流滑动时,UI thread 唤醒后应快速处理输入;如果同一时刻多个 worker 做图片解码,系统服务处理输入和合成,后台上传又保持 runnable,调度器需要在这些线程之间重新排序。

CPU affinity 和 cpuset 约束会改变“可运行”的含义。线程 runnable 只说明它有执行条件,仍需分配到允许的 CPU。后台线程可能被限制在低性能核心或较小 CPU 集合,前台交互线程可能获得更高性能核心机会。Android 的 task profile 和 cgroup 路径可以表达这类分组策略;Apple 的 QoS 和系统策略可以表达任务重要性和能耗取舍。具体设备还会受 SoC 拓扑、厂商策略和系统版本影响。

调度器分析的证据顺序应保持稳定:先看线程是否 runnable,再看 runnable 后多久获得 CPU,再看运行期间是否被频繁抢占,再看它所在进程或调度组是否被降级,再看 CPU 频率和核心选择是否受电源或温控改变。这个顺序能把“代码慢”和“排队久”分开,也能把“App 自己制造竞争”和“系统策略压低后台工作”分开。

28.4 Foreground Responsiveness 与交互线程优先级

前台响应性指用户发出输入后,系统在可感知时间内完成事件处理、状态更新、绘制提交和屏幕呈现。它属于输入分发、App 主线程、渲染链路、系统合成、调度器和硬件刷新共同输出的结果。移动 OS 会把前台交互线程放到更高调度位置,因为用户正在等待它。

一次滑动帧的关键路径通常是:触摸硬件产生事件,内核输入子系统和系统服务把事件交给前台窗口,App UI thread 执行回调并更新状态,渲染路径准备下一帧,系统合成器在刷新窗口前拿到 buffer 或 layer,display engine 扫描输出。路径中的每一步都有 deadline。60 Hz 屏幕大约每 16.67 ms 刷新一次,120 Hz 屏幕大约每 8.33 ms 刷新一次;App 不需要在这里记公式,只需要知道刷新率越高,前台线程可消耗的调度延迟越少。

Android 上,前台进程状态、top-app 调度组、输入分发超时、Choreographer 帧回调、RenderThread、SurfaceFlinger 等共同构成响应路径。公开开发文档从 App 角度强调主线程不能长时间阻塞;AOSP 和设备实现会把前台进程放入更高调度位置。这个组合说明一个事实:前台优先级需要 App 配合。系统可以提高 UI thread 获得 CPU 的机会,但无法替 App 拆掉主线程上的数据库查询、锁等待或同步网络请求。

Apple 平台上,主线程通常承载 UI 更新,GCD、OperationQueue、pthread 和 dispatch queue 可以携带 QoS。Apple 文档说明 App main thread 在 QoS 上属于 user-interactive,用户启动且需要短时间结果的工作适合 user-initiated,下载导入等可见但耗时工作适合 utility,后台同步、索引和备份适合 background。这个分层把用户可感知性直接映射为调度提示。

前台线程优先级还要处理优先级反转。典型场景是 UI thread 等待一个低优先级 worker 持有的锁。此时 UI thread 即使拥有高优先级,也只能等待 worker 释放锁。Apple QoS 文档专门讨论 priority inversion,并说明某些同步等待场景系统会尝试提升低优先级工作;Android / Linux 也有 mutex、rtmutex、binder 优先级传播等机制或实现细节,但具体行为依赖内核、IPC 和锁类型。本章的诊断结论是:看到高优先级线程 blocked,应追到被等待对象,而非停留在“前台优先级已经很高”。

在信息流滑动场景中,稳定做法是把 UI thread 的工作控制为输入解释、状态变更和提交绘制请求,把图片解码、网络解析、数据库读取放到受控 worker,把结果以异步方式回到主线程,并让 worker 的数量和 QoS / priority 与用户可见性一致。用户正在看见的图片可使用较高优先级,离屏预取和日志上报应使用较低优先级。这样系统调度器才能根据真实重要性分配 CPU。

判断前台响应问题时,先问四个问题:输入事件是否及时到达 App;UI thread 是否及时开始执行;UI thread 是否在帧截止前完成必要工作;render / compositor 是否按时提交和显示。四个问题分别对应输入服务、主线程、渲染链路和系统合成,能把“触摸不跟手”“主线程卡住”“绘制过慢”“合成或显示压力”拆开。

28.5 Background Task、Cached Process 与调度降级

后台任务和缓存进程是移动 OS 控制 CPU、公平性和续航的主要对象。App 离开前台后,系统会降低它对 CPU、网络、定时器、后台服务和进程保留的优先级;当它进入 cached 状态时,系统可以进一步减少执行机会,并在内存压力下回收进程。调度降级的目标是把有限资源让给用户当前正在操作的路径。

Android 文档把进程重要性分为 foreground、visible、service、cached 等层级,并说明 cached process 当前不被需要,系统可在资源需要时杀掉它;从 Android 13 开始,cached app process 可能获得有限或没有执行时间,直到进入活跃生命周期状态,Android process lifecycle 文档给出了这个版本边界。这个规则直接影响后台线程:线程还存在于进程中,不代表它能持续获得 CPU。

后台工作需要通过系统认可的入口表达用户价值。Android 中,前台服务、JobScheduler、WorkManager、AlarmManager、push 以及受限后台执行策略共同决定后台任务能否运行、何时运行、以什么优先级运行。Apple 平台中,background modes、BGTask、background URLSession、push、Low Power Mode 和 QoS 共同决定后台工作的执行窗口。两边共同点是:后台工作必须被系统纳入生命周期和能耗预算,长时间占用 CPU 的普通后台线程会被降级、暂停、延迟或在进程回收时终止。

缓存进程对响应性有双重影响。保留缓存进程能提升回到 App 时的热启动速度,因为代码、对象、缓存和状态仍在内存中;同时,缓存进程中的后台线程若继续运行,会消耗 CPU 和电量,影响前台 App。移动 OS 的策略是在这两个目标之间取舍:保存足够多的缓存进程提升切换体验,把执行机会集中给前台和可见工作,并在内存或功耗压力下回收缓存进程。

信息流 App 的后台预加载很容易越界。用户滑动时,当前屏幕图片解码属于前台体验链;下一屏预取属于 user-initiated 或 utility 范围;离屏很远的索引、日志压缩、批量同步属于 background 范围。把所有任务都提交到高优先级队列,会让 UI thread 与大量 worker 竞争 CPU;把当前屏幕必需解码放到过低优先级队列,会让 UI 等待后台结果。正确边界来自用户可见性,而非任务名称。

调度降级也会改变失败表现。前台时,同样的解码任务可能很快完成;进入后台后,它可能被延迟,甚至在 cached 状态下基本得不到执行时间;进程被回收后,任务状态必须靠持久化队列或系统调度 API 恢复。诊断后台任务丢失时,应先看它是否绑定了系统认可的后台执行模型,再看进程状态和系统资源压力,最后才看业务代码内部循环是否运行。

28.6 多核调度、大小核拓扑与移动功耗约束

手机 SoC 通常有多个 CPU 核心,并且常见大小核或性能域差异。大核提供更高峰值性能,也带来更高功耗和热量;小核提供更好的能效,适合持续或低优先级工作。调度器需要在响应速度、吞吐量、续航和温度之间做决策,因此“把任务放到最快核心”只覆盖短时性能目标,无法覆盖移动设备的完整约束。

Linux 的 Energy Aware Scheduling 文档说明,EAS 使用 CPU Energy Model 预测调度决策对能耗的影响,并主要面向 Arm big.LITTLE 这类异构拓扑;它会结合 CPU capacity、task utilization 和 performance domain 的能耗信息选择更节能的目标 CPU,Linux EAS 文档给出了公开模型。具体 Android 设备是否启用某种 EAS、如何叠加厂商策略、如何设置能耗模型,取决于 kernel 版本、SoC、OEM 调参和系统版本。

Android 多核调度会叠加 cgroup、uclamp、schedtune 或 task profile 等历史和版本相关机制。前台线程可能获得更高性能提示,后台线程可能被限制在较低容量核心或较低 CPU 利用率上限。系统服务、输入、渲染、音频等对延迟敏感的线程也会有自己的调度位置。读者需要抓住稳定模型:线程重要性、CPU capacity、当前负载、能耗模型、温度状态共同决定迁移和放置。

Apple Silicon 设备同样使用性能核心和能效核心组合。公开开发者材料通常通过 QoS、Low Power Mode、thermal state 和 Instruments 暴露上层信号,而不公开每个系统版本的内部核心选择策略。Apple 的 ProcessInfo.thermalState 是 App 可见的温度压力入口,开发者可以根据 nominal、fair、serious、critical 这类状态调整工作量;QoS 则表达任务与用户体验的关系。公开事实足以支持工程判断:把不可见工作放到较低 QoS,前台交互保留高 QoS,并在热压力上升时降低并发和刷新相关负担。

多核并行也会带来副作用。线程迁移会带来 cache locality 损失;多个 worker 并行会提升瞬时吞吐,也会推高电流和温度;热限制触发后,CPU 和 GPU 频率下降,原本足够快的任务会变慢;后台任务长时间满载小核,也可能影响系统服务和前台 App 的低延迟需求。移动 OS 调度的核心目标是让用户可见链路在电源和温度预算内稳定完成。

在信息流滑动场景中,当前可见图片解码可以短时使用较高性能资源;离屏预取应控制并发,防止把大小核都压满;日志和索引应延迟到系统更适合的窗口;持续滚动时应根据掉帧、温度和电量状态降低预取距离或图片质量。这个策略把 App 设计与系统调度目标对齐:用户正在等待的路径优先,后台吞吐服从前台稳定性。

多核调度诊断顺序是:先看 UI thread 或 render thread 是否被放在足够快的核心上,再看同进程 worker 是否制造 runnable 堆积,再看后台进程是否占用 CPU,再看温度或电源状态是否降低频率,再看迁移和 affinity 是否导致某些线程长期排队。这个顺序能解释同一段代码为什么在冷机、发热、充电、低电量、弱网和高刷新率下表现不同。

28.7 调度策略对 Jank、ANR、卡顿和发热的影响

Jank、ANR、卡顿和发热是同一组底层执行问题的不同用户可见结果。Jank 指帧没有按刷新节奏稳定呈现;ANR 指 Android 认为 App 在关键回调或输入处理上长时间无响应;卡顿是用户对输入延迟、动画不连续、页面停顿的统称;发热来自 CPU、GPU、modem、display、camera 等硬件持续高功耗,调度策略和任务并发会放大或缓解它。

主线程阻塞会同时解释 jank 和 ANR。信息流滑动时,UI thread 若执行数据库查询、同步解码、长 JSON 解析或等待锁,短时间内会错过帧截止,用户看到 jank;时间继续拉长,就可能进入 Android ANR 条件。Android 文档列出 input dispatching timeout、service callback 超时、foreground service 未及时调用 startForeground()、broadcast 超时、JobScheduler 回调超时等条件,这说明 ANR 是系统在特定边界上定义的无响应结果,底层原因仍需追到线程执行和等待关系。

CPU 饱和会制造调度延迟。App 主线程可能没有被锁阻塞,也没有执行重任务,但它从 runnable 到真正运行之间等待太久。原因可能是同进程 worker 数量过多,系统服务线程繁忙,后台进程仍占用 CPU,GC 或 JIT 等运行时线程活跃,或者内核线程正在处理网络、存储、内存回收。此时优化方向是减少并发、降低后台优先级、拆分突发任务、缩短临界区和控制每帧工作量。

Binder wait 或 XPC wait 会把跨进程问题表现成 App 卡顿。UI thread 调用系统服务后同步等待,系统服务若排队、被锁阻塞、等待 driver、等待另一个 daemon,App 就会显示为主线程 blocked。诊断时应沿着 wait chain 追踪:App 等哪一个 IPC,服务线程等哪一个锁或设备,内核对象是否有完成信号,超时结果由哪一层返回。移动 OS 的系统服务边界使这种等待很常见,因为 App 不能直接控制相机、位置、输入、窗口、通知和网络等能力。

Thermal throttle 会把调度问题变成持续性能下降。设备刚启动时同一条路径可能稳定 120 Hz;连续滚动、视频播放、弱网下载和后台索引叠加后,温度上升,系统降低 CPU / GPU 频率或改变核心选择,帧时间变长,worker 完成变慢,UI thread 等待结果的概率上升。Apple 暴露 thermal state,Android 设备也有 thermal service、thermal HAL、kernel thermal zone 和厂商策略。App 侧能做的判断是:当性能随温度上升持续下降时,应降低并发、刷新负担、预取距离、动画复杂度或媒体质量。

最终诊断顺序可以写成一条稳定路径:先确认用户可见现象属于丢帧、输入延迟、ANR、后台延迟还是发热;再定位用户等待的线程和进程;随后检查线程状态是 running、runnable 还是 blocked;再追踪 blocked 的等待对象或 runnable 的调度延迟;接着检查进程生命周期、调度组、QoS / priority、CPU affinity、大小核放置;最后加入 thermal、battery、memory pressure 和后台策略。这个顺序把 App 代码、系统服务、内核调度和硬件约束放在同一张责任图里。

对于本章的信息流场景,结论很具体:前台滑动优先保护 UI thread、render thread 和当前可见资源加载;后台预取、日志、同步和索引要降低优先级并限制并发;跨进程调用要从主线程同步路径中移走;热压力上升时要主动缩小工作量。系统调度器只能根据线程状态和平台策略做选择,App 需要提供清晰的工作分类和较短的前台临界路径。

最小自检任务

分析下面这个现象:一个新闻信息流 App 在冷启动后滑动流畅,连续滑动 5 分钟后开始掉帧,偶尔出现 Android ANR;抓到的现象包括 UI thread 有时等待图片解码结果,worker 线程数量随列表快速增长,后台日志上传持续运行,设备温度上升后同样的解码任务耗时增加。请写出从 App 到系统调度的路径,标出责任主体、资源边界、策略检查点和用户可见结果。

答案要点

请求路径应从用户触摸进入前台窗口开始,落到 App UI thread 处理输入和提交绘制,再连接 render thread / compositor、worker 图片解码、日志上传线程、系统服务和内核 scheduler。关键责任主体包括 App 主线程、App worker 线程池、图片加载模块、日志模块、输入和图形相关系统服务、内核调度器以及 thermal / power policy。

资源边界应区分进程容器与线程执行单元。App 进程承载 UI、worker、网络和数据库等线程;系统服务进程承载输入、窗口、图形或资源代理;内核负责 runnable queue、priority / QoS、cgroup 或 task profile、CPU affinity、preemption 和核心选择;硬件层提供大小核、频率和热状态约束。

策略检查点应包含前台进程优先级、UI thread 或 user-interactive QoS、worker 并发上限、后台日志任务优先级、cached / background 状态、CPU 放置和 thermal throttle。冷启动流畅说明单次代码路径可能满足帧预算;连续滑动后掉帧说明 runnable 堆积、主线程等待和热降频共同改变了调度结果。

核心结论应指出:ANR 的直接触发点在 Android 的输入或关键回调超时边界,用户先感知到的 jank 来自帧截止错过。根因链更可能是 worker 增长导致 CPU 饱和,UI thread 同步等待解码结果导致前台路径被后台工作绑定,日志上传继续消耗 CPU 和网络,温度上升后系统降频让每个任务更慢。修正方向是缩短 UI thread 临界路径、限制图片解码并发、把不可见预取和日志降级、用异步结果回主线程、在热压力上升时缩小预取和视觉负担。

本章知识点总结

  • 进程容器:进程承载 App、system service、daemon 和 extension 的代码、资源、身份和隔离边界。
  • 线程执行:调度器直接分配 CPU 给线程,前台响应分析要先找到用户正在等待的线程。
  • 主线程路径:UI thread 负责输入回调、状态更新和绘制触发,长时间阻塞会先导致 jank,继续拉长会进入无响应边界。
  • 回调线程:Binder、XPC、worker 和 render 相关线程会把 App 内部工作与系统服务等待连接起来。
  • 调度叠加:移动平台在内核调度之上叠加进程状态、cgroup / task profile、QoS、priority 和后台策略。
  • 前台优先:系统优先保障用户当前操作链路,但 App 仍需把主线程工作控制在短临界路径内。
  • 后台降级:后台任务和 cached process 会获得更少执行机会,并可能被延迟、暂停或回收。
  • 大小核约束:多核调度同时考虑 CPU capacity、task utilization、能耗、温度和迁移成本。
  • 热限制影响:thermal throttle 会降低可用频率或改变核心选择,使同样任务在发热后耗时增加。
  • 卡顿诊断:先定位用户可见现象,再定位线程状态,随后追踪等待对象、调度延迟、进程状态和硬件约束。
  • 跨进程等待:主线程同步等待系统服务时,App 卡顿可能来自另一进程、daemon、driver 或内核对象。
  • 工程取舍:当前可见工作应获得较高优先级,离屏预取、日志、索引和同步应降低优先级并限制并发。