腾讯:handler源码分析
handler:使APP不再crash
Android消息机制
主要 涉及到 Handler, Message, MessageQueue,Looper
以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。
Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。
在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); } } } }
|