Main 与 Main.immediate
选择正确的 UI 调度器,是保证 Android 60fps (16ms/帧) 流畅度的关键细节。
致命差异:Queue vs Direct
Dispatchers.Main(标准):- 总是使用
Handler.post()将任务扔进消息队列末尾。 - 后果: 哪怕你已经在主线程,它也会强行排队,等待下一次 Looper 轮询。这至少会延迟一帧的执行,甚至引发掉帧。
- 总是使用
Dispatchers.Main.immediate(推荐):- 智能判断: 先检查
Thread.currentThread()是否是主线程。 - 是: 直接执行 (Function Call),零延迟。
- 否: 退化为
Handler.post()。
- 智能判断: 先检查
实战:Why it matters?
场景:RecyclerView 绑定数据
kotlin
// 假设我们在 ViewModel 中加载数据
val data = repository.load()
// 切换回主线程赋值
withContext(Dispatchers.Main) {
// 这里会通过 Handler 发送消息
// 如果消息队列繁忙(例如正在处理复杂的 Touch 事件),
// UI 数据刷新会被延迟,用户会感到明显的“点击后迟钝”。
adapter.submitList(data)
}
withContext(Dispatchers.Main.immediate) {
// 立即赋值!UI 瞬间响应。
adapter.submitList(data)
}场景:动画回调
在 onAnimationUpdate 等高频回调中,由于已经处在主线程,使用 Main.immediate 能够确保每一帧的计算都能在 VSYNC 信号到来前完成,避免画面撕裂或卡顿。
最佳实践总结
铁律
除非你有明确理由(比如故意想把任务推迟到下一个 Message Loop),否则所有涉及 UI 操作的场景请无脑使用 Dispatchers.Main.immediate。
Jetpack 的 lifecycleScope 和 viewModelScope 默认使用的就是 Main.immediate。