故事:
话说有一天,Android的源码设计师,在多线程下对UI进行更新,发现是线程不安全的,并发下出现了UI控件处于不可预期的状态,于是加上了锁机制,但发现逻辑不仅更为复杂,而且对UI控件访问的效率大大降低,所以采用了仅限主线程可以访问UI控件,不然就会报异常。
接下来,子线程中获取了网络上的数据,想更新到UI上,发现不便,于是推出了消息机制。
一.子线程更新UI
这时候,有人却不想在主线程更新UI,于是去分析了源码,发现:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
- 这个方法存在于ViewRootImpl,在requestLayout()执行。
- requestLayout()执行顺序,是在onResume()方法之后。
所以,有人跑去onCreat() 方法里写了这串代码:
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("我就要在子线程更新UI");
}
}).start();
- 没错,他成功运行了起来。
二.让我们开始走进消息机制的源码世界
这是不是你们最常用的代码?
new Thread(new Runnable() {
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
};
}).start();
为何我们总是强调Looper.prepare()必须在new Handler()前面?
不信你去试试将new Hander()放在Looper.prepare(),系统说不行:
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
意思就是:不能在线程内部创建handler,因为他没有调用Looper.prepare()
让我们先留着这个疑问,到后面就告诉你答案。
一切的原理就从发送信息开始讲起
我们知道handler主要有两种方式传递信息:
(1)handler.sendMessage(msg)
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
(2)handler.post(Runnable run)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
handler.post(Runnable run)在getPostMessage®方法中将Runnable r 封装到Message的callback变量当中,返回Message对象。
然而(1)(2)这两种方式,都将执行Handler的**sendMessageDelayed(msg,delayMillis)**方法。
且让我们继续走进源码分析:
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 queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
是不是很简单的代码?没错。
在sendMessageDelayed(Message msg long delayMills)只是简单的将delayMills+SystemClock.uptimeMillis()换算成msg的执行时间。
然后继续执行sednMessageAtTime(消息,执行时间):在这个方法,我们发现了mQueue这个MessageQueue对象,这个对象是哪里来的呢?
然后我们来继续看源码:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
……
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
……
}
没错,从Hanlder的构造函数中,我们可以看到MessageQueue对象来自Looper。你是否对Looper.prepare()在Hanlder有猜想了呢?
让我们去看看Looper的源码:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里的sThreadLocal是TreadLocal对象,让我们走进源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
在这里我们不细讲ThredLocal和Thread以及THreadLocalMap之间的关系,你需要知道的是getMap(t)取出了当前线程中的ThreadLocalMap对象,ThreadLocalMap里面维护着一个数组Entry,类似键值对的方式保存一个对象。
这里保存的对象是什么呢?
这时候**Lopper.prepare()**方法正式上线.
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们发现了这样的异常:Only one Looper may be created per thread
意思就是:每个线程仅仅可以创建一个Looper对象。
在Looper.prepare()过程中,会判断Looper对象是否为空。
不为空,则抛出异常:Only one Looper may be created per thread
为空,则执行sThreadLocal.set(new Looper(quitAllowed)):
明显,这里新建了一个Looper对象:在这个构造函数中,绑定了新建的MessageQueue对象和当前线程。
让我们再去看看 sThreadLocal.set(new Looper(quitAllowed)) 源码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
就是这个set方法,将传进来的参数Looper对象保存到了当前线程中的TreadLocalMap中Entry数组 当前。
没错,就是它,我们之前所提到sThreadLocal.get()的那个对象,就是这个传进来的参数Looper对象
讲到这里,是不是我们可以理解为何Looper.prepare()必须在handler创建之前呢?
正如我们之前所说,Looper.prepare()将Looper与该Looper构造函数所创建的MessageQueue mQueue关联起来,然后将该Looper保存到了当前线程中的TreadLocalMap当中。
而在new Handler的时候:即
public Handler(Callback callback, boolean async) {
……
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
……
}
Looper.myLooper()就是取当前线程的ThreadLocalMap保存的Looper对象。
用到了该Looper.mQueue对象。不然,先new Handler()将得到一个空的MessageQueue,会报异常。
所以Looper.prepare()必须在handler创建之前。
那我们可以再回去继续看Hanlder的源码。刚才是从这里游出去的:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
继续走向handler的enqueueMessage方法。这里将msg.target指向了该handler对象,这handler非常重要,是后面Looper.loop()方法中msg.target.dispatchMessage()将会用到,是为了找到handler,将Message msg派遣出去使用的。
然后我们就走向了queue.enqueueMessage(msg,uptimeMills)
也就是我们的MessageQueue大佬的源码世界内来:(我们依旧只放出最关键的代码行)
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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);
}
}
return true;
}
我们可以看到,链表将以执行时间从小到大的顺序排列,形成一条单链表。没错,MessageQueue的全局变量 Message mMessages最后就将指向msg单链表的头部。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
不知道人家有没有注意到这行代码,是否知道这是什么意思?
首先我们得知道两个概念:屏障,异步消息。
屏障:常用于UI绘制前,插入一个屏障到消息队列的头部,所有同步消息都会被阻塞。屏障的target=null;
异步消息:我们sendMessage,post方法默认都是同步消息,如果我们设置message.setAsynchronous(true),那么message就是一个异步消息,不受屏障的限制。
没有屏障的时候,理会所有消息,把队列当做一个普通队列。
有屏障的时候,理会异步消息,把队列当做一个异步消息队列。
needWake=true,只有两种情况:
- message在消息队列的头部
- 消息队列头部是屏障的时候,只观察异步消息,message是异步消息的第一个
说白了,needWake=true,说明阻塞所需要等待的时间需要更新了。
讲到这里,我们已经理清了Handler Looper Message Message之间的·关系了吧
我们似乎只差最后一步了?我们将Message保存起来,就是为了发送出去的吧。没错,所以Loopoer.loop()出现了。
我们依旧来看看Looper的源码(只放出其中最关键的代码行):
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
}
}
就是这里取出MessageQueue对象,将单链表中的msg按照执行时间从小到大的顺序依次使用msg.target.dispatchMessage(msg)方法派遣出去。
这里的msg.target就是我们在hander enqueueMessagae()中的msg.target=this;就是发送这个msg的handler。
我们继续去看Handler的源码:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到 handleCallback(msg)和handleMessage(msg)
handleCacllback(msg)对应的就是我们handle.post(Runnable callback)方法的传递。msg对象封装着msg.callback。前面讲到post方法的时候我们已经提到过。
来看看**handleCallbck()**源码:
private static void handleCallback(Message message) {
message.callback.run();
}
对不对,就是我们post上去的run方法的执行。
handMessage(msg)对应的就是我们sendMessagae(msg)的传递。
来看看**handleMessage()**源码:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
对不对,就是我们new handler()的handleMessage(msg),要求子类必须实现这个接口去接收Message。
疑惑点:mCallback,这个对象是哪里来的呢?
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
当然还有一点,但由于不想前面显得太过繁琐,所以并没有在前面提及。
那就是Looper.loop()的阻塞问题。还记得上面的loop()这段代码吗?
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
在mQueue不为空的时候,我们这里不讲,也没必要。
我们需要了解的是,当Loop.loop()方法将mQueue维护的msg对象都派遣出去,只剩下一个null的msg对象的时候,为何没有执行loop()中的if(msg==null)?
msg对象为空不是跳出loop()方法了吗?然后application就关闭了?UI控件不响应了?
(UI的响应就是一种消息机制的体现)
我们先看看它的上一行
源码告诉我们queue.next()这方法可能会阻塞。这是为什么,让我们走进源码区看看:
(删减不少了,看不下去我也没办法)
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
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) {
if (now < msg.when) {
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
我们看看 nativePollOnce(ptr, nextPollTimeoutMillis)这个方法:
ptr是Natvie底层Looper地址。
nextPollTimeoutMillis是阻塞标识。
nextPollTimeoutMillis=-1,一直阻塞不会超时
nextPollTimeoutMillis=0,不会阻塞,立即返回
nextPollTimeoutMillis>0,最多阻塞nextPollTimeoutMillis毫秒。
当执行:
Message msg = queue.next(); // might block
链表头部msg为空的时候
首次循环,由于nextPollTimeoutMillis = 0;
执行nativePollOnce(ptr, nextPollTimeoutMillis)并不会阻塞,立即返回。
执行 nextPollTimeoutMillis = -1;
由于pendingIdleHandlerCount =0执行
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
在这里我们得到了mBlocked = true,将在新的消息来到时在enqueueMessage()用到。
接下来的代码并不执行,继续循环。
继续执行nativePollOnce(ptr, nextPollTimeoutMillis)
由于nextPollTimeoutMillis = -1,所以线程将一直阻塞。系统休眠,释放占用的资源。
直到有新的消息发送,执行MessageQueue对象的**enqueueMessage()**方法。
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
这段代码我其实在前面的**enqueueMessage()**源码中放了出来,但没有提及。
因为不提阻塞,其实提这个的意义并不大。
p=null (p就是之前的链表)
于是执行needWake=mBlocked=true
于是执行nativeWake(mPtr);
将唤醒之前一直被阻塞的nativePollOnce(ptr, nextPollTimeoutMillis)
继续执行**nativePollOnce(ptr, nextPollTimeoutMillis)**之后的代码,即:
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
因为msg!=null,now >=msg.when(这里我们当msg是即时信息)
于是return msg;
于是执行Message msg = queue.next(); // might block
msg还为空吗? 它就是新发来的消息。
当然会有其他的情况产生,并不一定会完全执行我们如上所说。
你或许会想到一个问题,就是如何才能结束Looper.loop()方法?
你可以注意到了在Messsage next()方法中有这样一段源码:
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
当mQuitting=true的时候,next()返回null,于是就执行了Loop loop()源码:
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
然后就可以结束这个loop()方法。
我们再去看看Looper源码,存在这么一个方法:
public void quit() {
mQueue.quit(false);
}
表面意思就是离开。
既然如此,我们就去看看MessageQueue的quit(false)源码:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
你可能看到了mQuitAllowed,是不是觉得有点眼熟,让我们看看之前的源码:
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));
}
默认是quitAllowed=true
quit(true)方法,确保了mQuitting = true;
于是当执行MessageQueue next()方法的时候,就执行如下源码:
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
跳出loop()方法,结束死循环。
所以我们可以使用Looper对象的quit()方法去结束Looper对象的loop()方法。
至于底层的nativeXXX(),idleHanlder,异步消息(紧急)我并没有多讲,当然是留给聪明的你去探索了。
至此,整个消息机制就完成了。至于主线程已经自带Looper了,我就不讲了。
总结:
每个线程仅仅可以创建一个Looper对象。
每个Looper对象可以对应多个hanlder对象。
每个Looper对象对应一个MessageQueue对象
每个MessageQueue维护一个Message链表
Message对象与发送消息的handler对象绑定。
Looper.prepare()执行在new Handler()前面
Looper.prepare()将新建的Looper对象保存在了当前线程的TreadLocalMap的entry数组当中。
Looper.loop()负责派遣MessageQueue还未派遣的msg对象。
handler.post(Runnable r):Runnable对象被封装到Message对象中,充当msg.callback。
使用Looper对象的quit()方法去结束Looper对象的loop()方法。