Android的消息机制(Handler的工作原理)
Android的消息机制
Android中的消息机制其实也就是Handler
的运行机制。Android中通过使用Handler
来进行更新UI的操作。因为Android的UI更新是单线程模型,UI控件也都是非线程安全的。其原因是如果给UI控件加锁,那么效率将会变得底下的同时还会将UI访问的逻辑变得复杂。
Handler的运行基于MessageQueue
和Looper
的支撑。MessageQueue
顾名思义是消息队列。但其实是一个单项链表结构来存储信息Message
的。而Looper
则是不断去读取消息队列MessageQueue
中的信息,将其交与发送该消息的Handler
去dispatchMessage
进行处理。
下面我们就Handler
、MessageQueue
和Looper
分别分析一下其各自的工作原理。
MessageQueue
消息队列中维护了一个Message
类型的变量mMessages
,Message
是链表结构,所以之前所说消息队列是通过单项链表结构来存储消息。
当消息队列收到来自Handler的消息时,消息队列需要将此条消息插入队列中也即mMessages
中。消息插入顺序和它的执行之间相关。
boolean enqueueMessage(Message msg, long 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. needWeke = 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()){ neesWake = false; } } msg.next = p; ///把消息插入到消息队列的头部 prev.next = msg; } if(neesWake){ nativeWake(mPtr); } } return true; }
主要是进行了消息的单链表插入,现在我们再来看下新的函数读取函数`next()。
...for(;;){ ... 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; }}
next()
方法中有一个无线循环,如果没有消息,next()
会一直阻塞。知道有消息来,会返回这条消息并从消息队列中移除。
在分析完了消息队列的插入和读取消息之后,我们应该再看下Looper
,但是在分析Looper
之前,我们需要先对ThreadLocal
有所了解。
ThreadLocal
ThreadLocal是一个线程内部的数据存储类。通过它可以在指定的线程内存储数据,其他线程无法访问到该线程的数据。当作用域是线程的时候可以使用ThreadLocal
。作用有两个:1.在指定线程存储数据。2.复杂逻辑下的对象传递。
ThreadLocal
会从各自线程内部取出一个数组,然后再从数组中根据当前ThreadLocal
的索引去查找出对应的Value的值。
public void set(T value){ Thread currentThread = Thread.currentThread(); Values valuse = Values(currentThread); if(valuse == null){ initializeValues(currentThread); } values.put(this, value);}
可以看到,通过当前线程拿到数组Values
,如果没有,则传入当前线程初始化一个数组。然后将值放到数组中。我们再看下是如何将值放到数组中的。
private void set(ThrealLocal<?> key, Object value){... Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } }
可以看到,ThreadLocal
的值存储位置永远都是ThreadLocal
的字段i
(值的hashCode
和长度减1的值做位运算)的下一个位置。
get
方法则是找出字段i
的值,然后从数组的i
的下一位获取到ThreadLocal
的值。
好了,讲解完了ThreadLocal
之后,我们需要分析一下Looper
的原理了。Looper
在消息机制中扮演消息循环的角色。通过不断的Loop()
去处理消息队列中的消息。我们先来分析Looper
的构造函数。
prite Looper(boolean quitAllowed){ mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
可以看到,初始化Looper
的时候,会内部构建一个消息队列。我们可以看到这个构造函数是一个私有函数,那我们应该如何为一个线程创建一个Looper
呢。我们可以通过Looper.prepare()
为线程创建一个Looper
。然后再通过Looper.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; // 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch took " + time + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } 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()
中是一个死循环,只有当消息队列的next()
方法返回为null
的时候才会退出。当Looper
的quit
方法调用的时候,MessageQueue
的quit
或quitSafely
方法会通知消息队列退出,这个时候消息队列的next()
就会返回为null
。而没有消息的时候next()
会一直阻塞,这样导致了loop
也一直阻塞。当有消息的时候,Looper
则会通过消息的target
找到发送消息的handler
然后调用其dispatchMessage(Message msg)
去将消息给Handler
处理。而dispatchMessage
是在创建Handler
时所使用的Looper
中执行的。所以就将逻辑代码转移到了指定的线程上执行了。
好了,大致分析了Looper
之后,我们再接着对Handler
做一个简单的了解。
Handler
Handler的工作主要是发送和接收消息,也即sendMessage
和dispatchMessage
两个函数。
在sendMessage
是将消息队列中插入一条消息,即调用enqueMessage
。MessageQueue
收到消息之后调用next()
将消息传给Looper
处理,然后Looper
又将消息丢给该Handler
来处理。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}
其中callback
是一个接口,是用来实例化Handler
时候的一个Runnable
对象。
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */public interface Callback { /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public boolean handleMessage(Message msg);}
主线程的消息循环
这里稍微再讲解一下主线程的消息循环。Android的主线程UI线程也就是ActivityThread
。其main()
方法中系统会通过Looper.prepareMainLooper()
来创建主线程的Looper
以及MessageQueue
。通过loop()
来开启主线程的消息循环。ActivityThread
中有一个Handler
来进行消息循环,这个Handler
就是AcitivytThread
内的ActivityThread.H
。
ActivityThread
通过ApplicationThread
和AMS
进行进程间通信,AMS
以进程间通信的方式完成ActivityThread
的请求后会回调ApplicationThread
的Binder
方法,然后ApplicationThread
会向H
发送消息,H
接收到消息后会将AplicationThread
中的逻辑切换到AcitivyThread
中执行,也就是切换到主线程执行,这个过程就是主线程的消息循环模型。
以上摘选自任玉刚的《Android开发艺术探索》加上自己的一丢丢记忆理解。
更多相关文章
- Android应用程序键盘(Keyboard)消息处理机制分析(1)
- Android - Android 的消息机制
- Android之进程与线程
- Android学习笔记:Android消息处理机制之Handler介绍
- Android之消息推送实现
- Android Handler机制 - handleMessage究竟在哪个线程执行