参考
hanlder 使用
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
//
}
}
};
Message message = Message.obtain();
message.what = 1;
message.arg1 = 1;
//1
handler.sendMessage(message);
//或者2
handler.sendMessageDelayed(message,1000);
//或者3
handler.post(new Runnable() {
@Override
public void run() {
}
});
接下来开始从源码开始分析,先看 Handler 的构造方法
Handler 源码分析
public Handler(Callback callback, boolean async) {
.....
mLooper = Looper.myLooper();
if (mLooper == null) { // 如果没有手动调用 Looper.prepare,则报错;主线程中默认有一个 Looper,子线程需要手动调用
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看下主线程在哪里初始 Looper,看到应用的入口 ActivityThread # main
初始化 Looper
public static void main(String[] args) {
Looper.prepareMainLooper(); // 初始化 Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop(); // 开始消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
接着分析, Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false); // 初始化,参数 false 表示不允许退出
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 从 sThreadLocal.get() 获取 looper
}
}
.....
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); // 创建一个 Looper 并放入 ThreaLocal 中
}
....
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 创建消息队列
mThread = Thread.currentThread(); // 当前线程
}
上面看到通过 prepare() 创建了 Looper,并把 Looper 放入ThreadLocal中。
接下来回到 ActivityThread # main,接着看 Looper.loop()
轮询消息队列
public static void loop() {
final Looper me = myLooper(); // 从 ThreadLocl 中获取一个 Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 从 Looper 中拿出消息队列
....
for (;;) { // 死循环
Message msg = queue.next(); // 从消息队列中,拿出消息
if (msg == null) {
return;
}
.....
try {
msg.target.dispatchMessage(msg); // 获取到消息,分发处理
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked(); // 回收处理完成的 Message,可以通过Message.obtain()复用
}
}
loop() 主要是从 ThreadLocal 里拿出当前线程的 Looper,然后又从 Looper 里面拿出消息队列 MessageQueue,不断轮询消息队列,如果有消息,则分发处理
接下来,我们看如何从消息队列中,拿出消息:queue.next()
从队列取出消息
Message next() {
final long ptr = mPtr;
if (ptr == 0) { // if the message loop has already quit and been disposed
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); // native 方法,应该是唤醒线程的
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { // target ==null,
do {
prevMsg = msg;
msg = msg.next; // 取出下一条消息
} while (msg != null && !msg.isAsynchronous()); // 查找异步的信息
}
if (msg != null) {
if (now < msg.when) {
// 延时处理,计算定时时的唤醒时间 ----下一个消息,将要阻塞的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取到一条消息,因为消息队列是链式结构,所以需要调整一下链表
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else { // 没有消息
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
.....
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
} // --- synchronized(this) end
for (int i = 0; i < pendingIdleHandlerCount; i++) {
// idleHandle 是消息队列空闲时才会执行的 Handler 。应用: leakcanary中检测内存泄漏的耗时任务会等到主线程空闲才执行
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
next(),主要任务是取出一条消息任务,如果消息任务有延时,会计算一下多久后执行,然后再下一次循环中,调用 native 方法进行一个类型闹钟的设置,时间到了会唤醒 next 方法。因为消息列表是单链表结构,所以取出一条消息后,链表需要移动。
处理消息
从消息队列 MessageQueue 拿到了下一条消息,回到 Looper.loop() 中通过 msg.target.dispatchMessage(msg) 处理消息,看到调用了message 的target,而这个 target 就是一个 Hanlder 对象,什么时候传进来的呢,稍后再看 。因此 loop 消息分发处理,我们回到了 Handler # dispatchMessage 中
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 调用Message中 callback(其实就是一个 Runnable)
// callback的传入是在调用 Message.obtain(Handler h, Runnable callback) 时
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { // mCallback是在创建 Handler 的时使用 Handler(Callback callback) 传入的
return;
}
}
handleMessage(msg); // 一般我们重写 Hanlder 的 handleMessage 方法
}
}
消息入队
上面分析了 Looper 初始化、Looper 轮询消息队列、取出消息队列并处理,还差一个消息添加到消息队列中。
在 Hanlder 中,看到很多添加消息的方法,但最终还是走了enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // message 的 target 就是当前的 handler 对象,msg.target.dispatchMessage(msg) 中的 target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); // 放入MessageQueue
}
接下来,看如何放入队列的 MessageQueue # enqueueMessage
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages; //列表头部
boolean needWake;
// 入队规则:队列没有消息、入队消息没有延时、入队消息的延时比队列头的延时早
if (p == null || when == 0 || when < p.when) { // 插入链表头部, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) { // 移动链表,插入合适的位置
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr); // 如果需要,调用 native 方法唤醒
}
}
return true;
}
希望分析完后,可以把下面的问题都能弄得明白
FAQ
Q: 说一下 hanlder 机制
从 Looper 初始化、消息循环、取出消息、处理消息、消息入队这几方面来说,如下:
a. 在应用启动的时候,也就是ActivityThread的main方法里面,创建了Looper和MessageQueue,然后调用Looper.loop 开启消息循环
b. 消息循环是这样的,调用 MessageQueue 的 next 方法,循环从消息队列中取出一条消息,然后交给Handler去处理,一般是回调handleMessage方法,取不到消息就阻塞,直到下一个消息入队或者其它延时消息时间到了就唤醒消息队列。
c. 消息入队,通过调用 handler 的 sendMessage 方法,内部是调用 MessageQueue 的 enqueueMessage 方法,进行消息入队。入队的规制是:队列没有消息,或者要入队的消息没有设置delay,或者delay时间比队列头的消息delay时间短,则将要入队的消息放到队列头,否则就插到队列中间,需要移动链表。
Q:ThreadLocal 是什么?
Threadlocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只能在指定线程中可以获取到存储的数据,对于其他的线程来说则无法获取到数据
Q:延时消息时如何处理的?
队列中没有消息,插入队列头;
队列不为空,则与队列头的消息比较一下延时,如果插入消息延时短,则插入消息头;
从规则看到,延时消息不是先延迟在发送,也是直接发送消息,而在 MessageQueue.next() 中,如果拿到一个延时消息,通过 Native 方法阻塞线程一定时间,等到消息的执行时间到后再取出消息执行
Q:那消息入队规则有什么好处呢?
延时越长的消息在队列越后面,因此消息循环时,next方法拿到一个延时消息时,如果判断时间没有到,则阻塞队列,不用管后面的消息。
Q:post、sendMessage 发送消息有什么区别
mHandler.sendMessage(message);
mHandler.post(new Runnable() {
@Override
public void run() {
// todo
}
});
post 一类的方法发送的是 Runnable 对象,但是其最后还是会被封装成 Message 对象,将 Runnable 对象赋值给 Message 对象中的 callback 变量,然后交由 sendMessageAtTime() 方法发送出去。
在处理消息时,会在 dispatchMessage() 方法里首先被 handleCallback(msg) 方法执行,实际上就是执行 Message 对象里面的 Runnable 对象的 run 方法。
而 sendMessage 一类的方法发送的直接是 Message 对象,处理消息时,在 dispatchMessage 里优先级会低于 handleCallback(msg) 方法,是通过自己重写的 handleMessage(msg) 方法执行。