源码的完全解析:Android的消息机制
故事:
话说有一天,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(){@Overridepublic 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) { 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{ Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } return true;}
我们可以看到,链表将以执行时间从小到大的顺序排列,形成一条单链表。没错,MessageQueue的全局变量 Message mMessages最后就将指向msg单链表的头部。
讲到这里,我们已经理清了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。
当然还有一点,但由于不想前面显得太过繁琐,所以并没有在前面提及。
那就是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()我并没有多讲,当然是留给聪明的你去探索了。
至此,整个消息机制就完成了。至于主线程已经自带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()方法。
更多相关文章
- android 学习七 一些xml layout组件的说明(自己总结不断更新)
- Android中的事件处理研究
- android线程 Handler Message Queue AsyncTask
- AsyncTask原理及不足
- 自定义View之滑动事件
- Android(安卓)NDK——必知必会之JNI和NDK基础全面详解(二)
- Android(安卓)本地文件缓存各个方法获取的路径小结
- 安卓(Android)+苹果(Ios)仿微信、陌陌 移动社交APP系统源码,手机
- android 设置Alpha值实现图片渐变效果