Untitled
Android滑的动点不动问题排查(Choreographer详解)
排查过程:
问题回顾:
在实习的时候,看到过一个问题,在APP内部,用户点击”展开更多回复”按钮无响应,内容无法展开,但在此过程中能够滑动页面,loading动画正常渲染,就只是分页加载不出来,等待10-20s之后会有时能加载出来。
初步排查:
- 首先排除主线程卡顿问题,因为能够滑动页面,动画也可以渲染。
- 是否是业务代码异步操作等逻辑造成?
- 通过打log来判断点击以后,是否通过RxJava异步卡顿造成,发现不是
- 动画一直流程,和日志也没有关系
- 是否是同步屏障导致?
同步屏障
同步屏障其实是和handler,渲染等有关的一个概念,总的来说就是在消息机制下,建立一个屏障,优先让异步消息先处理。这样说有点抽象,我们可以通过代码来理解
1 | public int postSyncBarrier() { |
其实在这里,我们看到了关键的
1 | msg.when = when; |
那message的这些关键词到底代表什么呢?
| 字段名 | 类型 | 作用 / 含义 |
|---|---|---|
what |
int |
消息的标识符,用于区分不同类型的消息。你可以自定义常量来表示不同的业务含义,例如 MSG_LOAD_MORE = 1、MSG_REFRESH = 2。 |
target |
Handler |
表示这条消息要发送给哪个 Handler。在 Handler.sendMessage(msg) 时会自动赋值为该 Handler 实例。 |
arg1 |
int |
用于传递一个整型参数,通常是附加数据。你自己定义含义,比如分页的页码、状态码等。 |
arg2 |
int |
也是整型参数,和 arg1 搭配使用。 |
obj |
Object |
可以附带一个任意类型的对象,比如你要传递一个 List、String、Bitmap 等。 |
replyTo |
Messenger |
用于进程间通信 IPC 的场景中,指明“回信”地址。 |
when |
long |
表示消息应该被处理的时间(毫秒)。通常由 Handler.postAtTime() 设置。 |
callback |
Runnable |
如果设置了该字段,则在消息被处理时,执行这个 Runnable 而不是走 Handler.handleMessage()。 |
这里做了一个总结,我们发现在源码中,并没有对target关键字进行赋值,也就是没有指定要发给哪个handler
所以默认是target == null, 它不会被任何 Handler 处理
Message对象通过next字段串联成单向链表,通过when,也就是不同的时机,插入到不同的位置。
在源码中,还标记了一个token,通过这个token来标记这个barrier。
1 | Message prev = null; |
这是一个按照 when 时间查找插入点的循环。如果 when = 0(立刻生效),会直接插入到最前面。否则会往后找,找到第一个 when > 当前 msg 的位置
1 | if (prev != null) { // 插入到 prev 和 p 之间 |
所以:同步屏障是按时间顺序插入到消息队列中的,不是简单加到头或尾。
那既然是让异步的消息来处理,那什么是异步消息,同步消息呢,为什么需要内存屏障呢?
在 Android 的 MessageQueue 中:
- 同步消息(默认):
message.target != null && !message.isAsynchronous()
→ 会被同步屏障(sync barrier)阻塞,不能通过 barrier - 异步消息:
message.isAsynchronous() == true
→ 可以穿越同步屏障,优先被处理(用于动画、输入、绘制)
其实就是为了动画能够更快的进入加载,这个过程其实是指的动画等doframe与vsncy的一个机制
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.