Android 的Handler、Looper和MessageQueue的关系和实现原理
2015-12-10 17:28
597 查看
最近比较闲,看了一些关于android消息机制的书籍和文摘,写个文档总结下。
一、基本概念
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message
msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
二、Handler、looper和消息队列的关系
2.1、从一个简单的程序说起
代码段1:
但是如果我们确实有在工作线程运行的过程中去处理UI的需求该怎么办呢?
我们将代码段1稍作修改:
代码段2:
此时我们再运行并点击按钮,发现文本设置成功。
那么问题来了:
问题1:handler.sendEmptyMessage做了什么?
2.2、解析Handler的消息传递(问题1)
首先我们进入sendEmptyMessage,并依次找到被调用的函数,代码如下:
且当queue == null时,会弹出以“Looper”作为Tag的异常信息;
如果queue != null时,通过queue.enqueueMessage将消息交给了消息队列。
上面我们解答了问题1,此时我们又有以下问题:
2、Looper是什么东西,有什么作用?
3、这个消息队列是怎么来的?
2.3、Handler与Looper的关系
Handler有两个主要的构造函数,在这里贴上默认构造函数对应的代码:
Looper.myLooper()的实现如下:
2.4、浅尝[b]sThreadLocal[/b]
既然到了这里,我们再来看一下sThreadLocal是个什么玩意,其定义如下:
好了,sThreadLocal的探讨就先到这里,我们还是回到原来的地方;
2.4、Handler、looper和消息队列的关系
通过2.3我们大概知道了Handler和looper的关系,而且也知道消息队列来自于looper,为了肯定这一点,我们查看了源码,如下:
一句话:handler通过looper获取消息队列
由于主线程中默认有looper,而子线程没有,我们接下来在子线程更加详细的说明3者的关系
三、Looper的原理(问题2)
3.1、looper.prepare的作用
修改代码片段1的click函数如下:
只是添加了一行代码,运行并点击按钮,会报错,错误信息如下:
意思就是,没有调用Looper.prepare(),这个是干神马的呢?看源码如下:
通过源码知道,prepare创建了looper,并且将其保存在sThreadLocal中,通过注释理解到创建完成后要调用loop函数;
注意:主线程创建Handler并不需要手动创建looper,是因为主线程在初始化的main函数中调用Looper.prepareMainLooper()生成了looper;
3.2、Looper.loop的作用
贴源代码:
那么问题来了
问题4:以上源代码可知、不发送消息,loop调用后是不是循环就立马终止呢?
我们先来看看dispatchMessage的源代码:
3.3、正常创建消息循环
说了这么多,那么我们就看如何在工作线程中创建消息循环吧
四、消息队列机制
从2.2的最好一个片段的代码,我们知道,handler最终将消息放入到了消息,队列中,那么消息队列又是怎样存储消息的呢?
4.1、消息存储机制
MessageQueue的enqueueMessage源码如下:
4.2、消息获取机制
从3.2中的loop函数,我们可以得知,消息队列调用类next函数来获取消息,MessageQueue的next源码如下:
由此可知,当mQuitting为false时,不管有没有消息,for循环都不会结束,如果消息队列中有消息,则返回消息,如果没有一直循环,不返回;
那么3.2节中的问题4的答案就出来了,当没有消息,且没有调用Looper.quit时,消息队列的next函数会一直阻塞,那么loop函数也会阻塞;
参考的资料:
1、http://blog.csdn.net/liuhe688/article/details/6407225
一、基本概念
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message
msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
二、Handler、looper和消息队列的关系
2.1、从一个简单的程序说起
代码段1:
</pre><pre name="code" class="java">public class MainActivity extends Activity{ private TextView mText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView)findViewById(R.id.myText); } public void click(View view) { new Thread(new Runnable() { @Override public void run() { // 耗时的操作...... // 设置UI mText.setText("padmatek"); } }).start(); } }布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.padmatek.liang.myhandler.MainActivity"> <TextView android:id="@+id/myText" android:text="0" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:textSize="30sp"/> <Button android:id="@+id/myBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试" android:layout_below="@id/myText" android:layout_centerHorizontal="true" android:onClick="click"/> </RelativeLayout>运行并点击“测试”按钮,app崩溃,报错如下;原因是因为android中UI控件不是线程安全的,所以我们不能在工作线程(非UI)中访问UI控件
但是如果我们确实有在工作线程运行的过程中去处理UI的需求该怎么办呢?
我们将代码段1稍作修改:
代码段2:
public class MainActivity extends Activity{ private TextView mText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView)findViewById(R.id.myText); } private final static int MSG_SET_TEXT = 0; private Handler handler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_TEXT: mText.setText("padmatek"); break; default: break; } } }; public void click(View view) { new Thread(new Runnable() { @Override public void run() { // 耗时的操作...... // 设置UI handler.sendEmptyMessage(MSG_SET_TEXT); } }).start(); } }在该代码段中,我们创建了一个handler,并将按钮处理函数的设置Text的语句改为了使用handler发送一个消息;
此时我们再运行并点击按钮,发现文本设置成功。
那么问题来了:
问题1:handler.sendEmptyMessage做了什么?
2.2、解析Handler的消息传递(问题1)
首先我们进入sendEmptyMessage,并依次找到被调用的函数,代码如下:
public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return 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); }从函数sendMessageAtTime里面蹦出来个mQueue
且当queue == null时,会弹出以“Looper”作为Tag的异常信息;
如果queue != null时,通过queue.enqueueMessage将消息交给了消息队列。
上面我们解答了问题1,此时我们又有以下问题:
2、Looper是什么东西,有什么作用?
3、这个消息队列是怎么来的?
2.3、Handler与Looper的关系
Handler有两个主要的构造函数,在这里贴上默认构造函数对应的代码:
public Handler() { this(null, false); }其中this对应的实现如下:
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }从以上代码可以看出,消息队列mQueue 来自于mLooper(问题3) ,而mLooper 又来自于Looper.myLooper();
Looper.myLooper()的实现如下:
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }看到这里,是不是有点“拔出萝卜带出泥”的感觉......
2.4、浅尝[b]sThreadLocal[/b]
既然到了这里,我们再来看一下sThreadLocal是个什么玩意,其定义如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper;而其get函数的定义如下:
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }由于sThreadLocal为类静态变量,说明他是全局有效的,说明我们的所有的looper应该都保存在sThreadLocal中,所以get的实现原理大概就是通过线程句柄来获取对应的looper;
好了,sThreadLocal的探讨就先到这里,我们还是回到原来的地方;
2.4、Handler、looper和消息队列的关系
通过2.3我们大概知道了Handler和looper的关系,而且也知道消息队列来自于looper,为了肯定这一点,我们查看了源码,如下:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }到现在为止,我们说了这么多,是不是有点小晕了,反正我是有点,那么放下脚步小结一下:
一句话:handler通过looper获取消息队列
由于主线程中默认有looper,而子线程没有,我们接下来在子线程更加详细的说明3者的关系
三、Looper的原理(问题2)
3.1、looper.prepare的作用
修改代码片段1的click函数如下:
public void click(View view) { new Thread(new Runnable() { @Override public void run() { // 耗时的操作...... // 设置UI handler.sendEmptyMessage(MSG_SET_TEXT); Handler handlerSub = new Handler(); } }).start(); }
只是添加了一行代码,运行并点击按钮,会报错,错误信息如下:
意思就是,没有调用Looper.prepare(),这个是干神马的呢?看源码如下:
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }
通过源码知道,prepare创建了looper,并且将其保存在sThreadLocal中,通过注释理解到创建完成后要调用loop函数;
注意:主线程创建Handler并不需要手动创建looper,是因为主线程在初始化的main函数中调用Looper.prepareMainLooper()生成了looper;
3.2、Looper.loop的作用
贴源代码:
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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }由以上源代码可知,loop方法是一个死循环,不断地从消息队列中取消息,将取出的消息交给msg.target的
dispatchMessage处理,其中msg.target就是我们创建的handler. 如果消息队列返回为null,则跳出loop,终止获取消息.
那么问题来了
问题4:以上源代码可知、不发送消息,loop调用后是不是循环就立马终止呢?
我们先来看看dispatchMessage的源代码:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }如果msg带的callback和Handler的们Callback都为空,则调用handleMessage来处理消息,也就是代码片段1handleMessage函数;
3.3、正常创建消息循环
说了这么多,那么我们就看如何在工作线程中创建消息循环吧
public void click(View view) { new Thread(new Runnable() { @Override public void run() { // 耗时的操作...... // 设置UI handler.sendEmptyMessage(MSG_SET_TEXT); Looper.prepare(); Handler handlerSub = new Handler(); Looper.loop(); } }).start(); }这样子,就可以在子线程中通handler来发送和处理消息了,自己去实现吧。
四、消息队列机制
从2.2的最好一个片段的代码,我们知道,handler最终将消息放入到了消息,队列中,那么消息队列又是怎样存储消息的呢?
4.1、消息存储机制
MessageQueue的enqueueMessage源码如下:
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; }从源码可知,消息按照其delay的时间从小到大保存在一个链表中,并且消息保存时线程安全的;
4.2、消息获取机制
从3.2中的loop函数,我们可以得知,消息队列调用类next函数来获取消息,MessageQueue的next源码如下:
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } 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 && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } 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 first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { 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); } } } // 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; } }
由此可知,当mQuitting为false时,不管有没有消息,for循环都不会结束,如果消息队列中有消息,则返回消息,如果没有一直循环,不返回;
那么3.2节中的问题4的答案就出来了,当没有消息,且没有调用Looper.quit时,消息队列的next函数会一直阻塞,那么loop函数也会阻塞;
参考的资料:
1、http://blog.csdn.net/liuhe688/article/details/6407225
相关文章推荐
- SoapUI + Groovy 接口自动化
- 155 Which three statements are true about windows? (Choose three.) A. Only one window can be open at
- 154 Which three statements are true about persistent configuration? (Choose three.) A. A user cannot
- hibernate第一天——分页显示,数字类型互相转换intvalue(crl+h),hibernate的配置与API,建立表结构,final类型,映射文件,主键
- iOS-UIKit(UIScrollView.h - -解读)
- StringBuilder与StringBuffer的区别
- 装系统心得
- BlueTooth: 蓝牙协议栈实现模式分析
- Easy UI Combotree选中项样式问题的hack
- UIButton文字位置显示
- iOS多线程编程之NSOperation和NSOperationQueue的使用
- android PriorityQueue优先级队列解析
- UIViewController的生命周期
- iOS之UI--主流框架的搭建--仿制QQ的UI框架
- ios开发——NSdata 与 NSString,Byte数组,UIImage 的相互转换
- EasyUI常用控件禁用方法
- SaltStack 最新版WebUI部署
- suid,sgid,sticky_bit 特殊权限-note
- [MD学习]使用ActionBar+DrawableLayout+Palette打造侧滑UI
- UItextField