前面已经写过handler的使用方法了 -> Android Handler全使用,本篇将详解handler机制原理和源码。
Message
线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。使用Message的arg1和arg2便可携带int数据,使用obj便可携带Object类型数据。
Handler
消息的处理者。在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,完成消息对象Message的处理。
MessageQueue
消息队列。只存放所有通过Handler发送过来的Message消息对象。这部分消息会一直存放于消息队列当中,等待被处理。
每个线程中只会有一个MessageQueue对象。
MessageQueue中有两个比较重要的方法,一个是enqueueMessage方法,一个是next方法。enqueueMessage方法用于将一个Message放入到消息队列MessageQueue中,next方法是从消息队列MessageQueue中阻塞式地取出一个Message。
Looper
MessageQueue的管家。每个线程只有一个Looper对象。调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当MesssageQueue中存在一条消息,Looper就会将这条消息取出,并将它传递到Handler的handleMessage()方法中。
注意:
- 一个线程只有一个
Looper
,但可以有多个处理者Handler
- 一个
Looper
可绑定多个处理者Handler
,但一个处理者Handler
只能绑定一个Looper
工作流程图
源码解析
1、handler的构造方法
public Handler() {
this(null, false);
}
...
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
由上可知,创建handler时
1)会自动关联线程中的Looper对象(绑定线程),若线程无Looper对象则抛出异常。
若线程中无法创建Looper对象,则也无法创建Handler对象,因此若需要在子线程中创建Handler对象,则需先创建Looper对象。
2)绑定MessageQueue对象,至此,handler对象关联上了 Looper对象中的MessageQueue
那么这里的Looper对象怎么来的呢,什么时候创建的?
2、Looper
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepare() {
prepare(true);
}
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
看Looper构造方法,可知Looper创建一个对象时就自动创建一个当前线程的MessageQueue对象。
从Lopper.prepare()方法可看到,为当前线程创建Looper对象,存放在ThreadLocal变量中。当我们需要在子线程中创建Looper时便需要调用该方法(在子线程若不手动创建Looper
对象 则无法生成Handler
对象)
注意:这里有个方法 prepareMainLooper,为主线程创建Looper对象。该方法在主线程(UI线程)创建时自动调用:
// ActivityThread类
public static void main(String[] args) {
...
// 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
Looper.prepareMainLooper();
// 2. 创建主线程
ActivityThread thread = new ActivityThread();
// 3. 自动开启 消息循环
Looper.loop();
}
即主线程的Looper对象自动生成,无需手动生成。
生成Looper
& MessageQueue
对象后,则会自动进入消息循环:Looper.loop(),源码如下:
public static void loop() {
...
final Looper me = myLooper();//返回sThreadLocal存储的Looper实例
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取Looper实例中的消息队列对象(MessageQueue)
final MessageQueue queue = me.mQueue;
// 消息循环
for (;;) {
Message msg = queue.next(); // 取出消息
// 若取出的消息为空,则线程阻塞
if (msg == null) {
return;
}
// 派发消息到对应的Handler,把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
msg.target.dispatchMessage(msg);
// 释放消息占据的资源
msg.recycle();
}
}
注意,这里两个主要操作:取出队列中的消息对象--queue.next(),分发消息给handler--dispatchMessage(msg);
看一下两个方法源码:
Message next() {
...
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
...
}
...
}
}
public void dispatchMessage(Message msg) {
// 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里run()方法
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息
// 则执行handleMessage(msg),即回调复写的handleMessage(msg)
handleMessage(msg);
}
}
可以看到,取出dispatchMessage()执行后就回到我们熟悉的run方法或者handleMessage方法了!
3、Message
那么Message对象是怎么创建的呢,在上节handler的使用中,我们已经说明,是通过obtain方法创建的。
public static Message obtain() {
// Message内部维护了1个Message池,用于Message消息对象的复用
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
当然,我们也可以new一个message,不过更推荐使用obtain()创建消息对象,避免每次都使用new重新分配内存.
4、工作线程发送消息至消息队列
我们在工作线程中,发送了消息,
当使用方式为:mHandler.sendMessage(msg),发生了什么呢?
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 调用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 将msg.target赋值为this,即把当前的Handler实例对象作为msg的target属性
// 因此,在loop()循环中才是msg.target执行dispatchMessage(msg)方法
msg.target = this;
// 调用消息队列的enqueueMessage(),Handler发送的消息,最终是保存到消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
//将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
//采用单链表实现:提高插入消息、删除消息的效率
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判断消息队列里有无消息
// 若无,则将当前插入的消息作为队头 & 若此时消息队列处于等待状态,则唤醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 若有,则根据 消息(Message)创建的时间插入到队列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage将消息对象message插入到队列之后,随着Looper对象的无限消息循环,不断从消息队列中取出Handler发送的消息,并且分发到对应Handler,最终回调Handler.handleMessage()处理消息。
当选择post方式发送消息时,又是怎样的呢?
实际上最终调用方式是一样的:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
// 系统帮我们自动创建一个message对象
Message m = Message.obtain();
// 将 Runable对象 赋值给消息对象(message)的callback属性
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
后面的就和上面sendMessage方式分析的一样的了。
好了,原理和源码都已讲完,下一篇我将根据这篇内容,尝试手写一套handler机制!敬请期待!