谈Handler

腾讯:handler源码分析

handler:使APP不再crash

Android消息机制

主要 涉及到 Handler, Message, MessageQueue,Looper

  1. 以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。

  2. Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。

  3. 在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

消息阻塞和延时

Looper 的阻塞主要是靠 MessageQueue 来实现的,在MessageQuese#next 进行阻塞,在 MessageQueue#enqueueMessage 进行唤醒。主要依赖 native 层的 Looper 依靠 epoll 机制进行的。

阻塞和延时,主要是next中nativePollOnce(ptr, nextPollTimeoutMillis)调用naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞nextPollTimeoutMillis为0的时候表示不阻塞,为-1的时候表示一直阻塞直到被唤醒,其他时间表示延时。

主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源

其他

一个线程只绑定一个Looper,所以在Looper构造方法里面初始化就可以保证mQueue也是唯一的Thread对应一个Looper 对应一个 mQueue。

Handler.post的逻辑在哪个线程执行的,由Looper所在线程决定的。逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。

手写handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

package com.handlerr;


public class Handler {


private final Looper looper;
private final MssageQueue queue;

public void handMessage(Message message) {
}

public Handler() {

looper = Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException("should call looper.prepare first");
}

queue = looper.mQueue();
}

public void sendMessage(Message message) {
message.target = this;
queue.equeueMessage(message);
}

public void run(Runnable runnable) {
Message message = new Message();
message.callback = runnable;

queue.equeueMessage(message);
}


public void dispatch(Message next) {
if (next.callback != null) {
next.callback.run();
} else {
handMessage(next);
}
}

}


1
2
3
4
5
6
7
8
9
10
11
12
package com.handlerr;

public class Message {

public int what;
public Object obj;

public Handler target;

public Runnable callback;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

package com.handlerr;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MssageQueue {

private BlockingQueue<Message> mQueue = new LinkedBlockingQueue<>();

public MssageQueue() {
}

public Message next() {
try {
Message take = mQueue.take();
return take;
} catch (InterruptedException e) {
e.printStackTrace();
}

return null;
}

public void equeueMessage(Message message) {
mQueue.add(message);
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.handlerr;

public class Looper {


private static ThreadLocal<Looper> sThreadLocal = new ThreadLocal();

MssageQueue queue;

private Looper() {
queue = new MssageQueue();
}

public static void prepare() {
sThreadLocal.set(new Looper());
}

public static final Looper myLooper() {
return sThreadLocal.get();
}

public MssageQueue mQueue() {
return queue;
}


public static void loop() {

while (true) {

Message next = Looper.myLooper().mQueue().next();

if (next != null) {
next.target.dispatch(next);
}
}
}
}