作者:刘昊昱

博客:http://blog.csdn.net/liuhaoyutz

Android版本号:4.4.2

在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们来分析一下其背后隐藏的Android消息处理机制。

我们可能比較熟悉Windows操作系统的消息处理模型:

while(GetMessage(&msg,NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

1、消息被投递到消息队列中。

2、应用程序在消息处理循环中调用GetMessage函数从消息队列中取出消息(直到取得WM_QUIT消息,才会退出消息处理循环)。

3、调用TranslateMessage函数对消息进行必要的处理,比如放弃对某些消息的响应。

4、调用DispatchMessage函数将消息分配给相应对象处理。

Android作为图形化的操作系统,其消息处理机制与Windows是类似的,相同有一个消息处理循环从消息队列中取出消息,然后将消息发送给对应的对象处理。

在Android系统中,消息处理循环相应的类是Looper,消息相应的类是Message,消息队列相应的类是MessageQueue,消息的处理和分发是通过Handler类完毕的。

首先我们来分析Looper,该类定义在frameworks/base/core/java/android/os/Looper.java文件里。

Android文档对Looper类的说明例如以下:

Class used to run amessage loop for a thread. Threads by default do not have a message loopassociated with them; to create one, call prepare() inthe
thread that is to run the loop, and then loop() tohave
it process messages until the loop is stopped.

Most interaction with amessage loop is through the Handler class.

This is a typicalexample of the implementation of a Looper thread, using the separation of prepare() and loop() tocreate
an initial Handler to communicate with the Looper.

  class LooperThread extends Thread {
      public Handler mHandler;       public void run() {
          Looper.prepare();           mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };           Looper.loop();
      }
  }

从上面Android文档对Looper类的说明中能够看到,对于一个线程,要创建消息处理循环,须要调用Looper.prepare()函数和Looper.loop()函数。当中,Looper.prepare()完毕必要的初始化工作,Looper.loop()完毕循环取消息,分发消息的工作。

先来看Looper.prepare()函数。

71     /** Initialize the current thread as alooper.
72 * This gives you a chance to create handlers that then reference
73 * this looper, before actually starting the loop. Be sure to call
74 * {@link #loop()} after calling this method, and end it by calling
75 * {@link #quit()}.
76 */
77 public static void prepare() {
78 prepare(true);
79 }
80
81 private static void prepare(boolean quitAllowed) {
82 if (sThreadLocal.get() != null) {
83 throw new RuntimeException("Only one Looper may be created perthread");
84 }
85 sThreadLocal.set(new Looper(quitAllowed));
86 }

78行,调用prepare(true),參数true表示同意该Looper退出循环。UI线程也有Looper,而UI线程的Looper是不同意退出的。

82行和85行分别调用了sThreadLocal的get和set成员函数。首先我们来看sThreadLocal的定义:

58    static final ThreadLocal<Looper>sThreadLocal = new ThreadLocal<Looper>();

可见,sThreadLocal是模板类ThreadLocal的实例化对象。ThreadLocal<T>模板类定义在libcore/luni/src/main/java/java/lang/ThreadLocal.java文件里。

我们来看Android文档对ThreadLocal模板类的说明:

Implements a thread-local storage, that is, a variable forwhich each thread has its own value. All threads share the same ThreadLocal object,
buteach sees a different value when accessing it, and changes made by one threaddo not affect the other threads. The implementation supports null values.

ThreadLocal对象实现了线程的私有数据存储,当中保存的数据仅仅有本线程自己能够訪问,其他线程无法操作。ThreadLocal.set(T)设置了当中保存的数据为T。ThreadLocal.get()取得当中保存的数据。

这样我们就能够理解prepare函数了。82行,假设sThreadLocal.get()返回值不为null,说明该线程已经有一个Looper了,每一个线程仅仅同意有一个Looper,所以抛出一个异常退出。假设该线程还没有Looper,则运行85行,调用sThreadLocal.set(newLooper(quitAllowed)),new一个Looper,保存在sThreadLocal中。这样,通过调用prepare函数,就为该线程创建一个Looper对象。

另一点须要说明的是,调用new Looper(quitAllowed)时,会创建该线程的消息队列,来看Looper的构造函数:

220    private Looper(boolean quitAllowed) {
221 mQueue = new MessageQueue(quitAllowed);
222 mThread = Thread.currentThread();
223 }

221行,new一个MessageQueue保存在mQueue变量中,即该线程的消息队列。

223行,取得当前线程保存在mThread变量中。

这样,再总结一次,通过调用Looper.prepare()函数,我们就为本线程创建了Looper,同一时候也创建了MessageQueue。

分析完Looper.prepare()函数,我们再来看Looper.loop()函数:

112    /**
113 * Run the message queue in this thread. Besure to call
114 * {@link #quit()} to end the loop.
115 */
116 public static void loop() {
117 final Looper me = myLooper();
118 if (me == null) {
119 throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");
120 }
121 final MessageQueue queue = me.mQueue;
122
123 // Make sure the identity of thisthread is that of the local process,
124 // and keep track of what that identitytoken actually is.
125 Binder.clearCallingIdentity();
126 final long ident =Binder.clearCallingIdentity();
127 LocalLog localLog = me.mLocalLog;
128 if (localLog != null) me.mStringBuilder= new StringBuilder();
129
130 for (;;) {
131 Message msg = queue.next(); //might block
132 if (msg == null) {
133 // No message indicates thatthe message queue is quitting.
134 return;
135 }
136
137 long dispatchStart = 0;
138 // This must be in a localvariable, in case a UI event sets the logger
139 Printer logging = me.mLogging;
140 if (logging != null) {
141 logging.println(">>>>> Dispatching to " +msg.target + " " +
142 msg.callback + ":" + msg.what);
143 }
144 if (localLog != null) {
145 me.mDispatching =msg.toStringLw();
146 me.mDispatchStart =SystemClock.uptimeMillis();
147 }
148
149 msg.target.dispatchMessage(msg);
150
151 if (logging != null) {
152 logging.println("<<<<< Finished to " +msg.target + " " + msg.callback);
153 }
154 if (localLog != null) {
155 final long elapsed =SystemClock.uptimeMillis() - me.mDispatchStart;
156 final long wait =me.mDispatchStart - msg.when;
157 me.mStringBuilder.setLength(0);
158 if (elapsed >=LATENCY_THRESHOLD) {
159 me.mStringBuilder.append("WARNING! ");
160 }
161 me.mStringBuilder.append("Wait: ")
162 .append(wait)
163 .append("ms, Run: ")
164 .append(elapsed)
165 .append("ms due Message")
166 .append(me.mDispatching);
167 localLog.log(me.mStringBuilder.toString());
168 me.mDispatching = null;
169 }
170
171 // Make sure that during the courseof dispatching the
172 // identity of the thread wasn'tcorrupted.
173 final long newIdent =Binder.clearCallingIdentity();
174 if (ident != newIdent) {
175 Log.wtf(TAG, "Threadidentity changed from 0x"
176 +Long.toHexString(ident) + " to 0x"
177 +Long.toHexString(newIdent) + " while dispatching to "
178 +msg.target.getClass().getName() + " "
179 + msg.callback + "what=" + msg.what);
180 }
181 msg.recycle();
182 }
183 }

117-127行,准备工作,取得本线程的Looper和MessageQueue等元素。

130-182行,这个for循环即完毕循环取出消息,分发消息的工作。

131行,调用MessageQueue.next()函数,从MessageQueue中取得一个Message。MessageQueue.next()函数可能会堵塞,其返回值仅仅有两种可能,一是返回取得的Message,或者返回null。假设返回null,表示退出消息循环。

149行,调用msg.target.dispatchMessage(msg);完毕消息的分发。

Message.target是Handler对象,所以149行,就是调用Message相应的Handler对象的dispatchMessage函数。如今问题是Message相应的Handler是如何指定的。

回顾一下上一篇文章《Android架构分析之Android消息处理机制(一)》,我们发送一个Message的过程是先new一个Message,然后调用Handler.sendMessage函数将Message插入本线程消息队列的尾部。过程非常easy,所以,我们有理由相信,非常可能是在Handler.sendMessage函数中,为Message.target指定了相应的Handler。

在分析Handler之前,我们要再次理解Android的消息处理流程:

1、      Handler通过Handler.sendMessage函数,将Message发送到本线程的MessageQueue中。

2、      本线程的Looper不断循环读取本线程的MessageQueue,从中取出下一条Message,然后调用Message.target.dispatchMessage函数,将消息发送到相应的处理函数。而当中一个可能的处理函数就是Handler.handleMessage,应用程序通常会重载实现这个函数。

以下我们来看Handler的实现,其类定义在frameworks/base/core/java/android/os/Handler.java文件里。

首先我们来看Android对Handler的说明:

A Handler allows you tosend and process Message andRunnable
objects associated with a thread's MessageQueue.
EachHandler instance is associated with a single thread and that thread's messagequeue. When you create a new Handler, it is bound to the thread / message queueof the thread that is creating it -- from that point on, it will delivermessages and runnables to
that message queue and execute them as they come outof the message queue.

There are two main usesfor a Handler: (1) to schedule messages and runnables to be executed as somepoint in the future; and (2) to enqueue an action to be performed on adifferent thread than your own.

Scheduling messages isaccomplished with the post(Runnable)postAtTime(Runnable,
long)
postDelayed(Runnable,
long)
sendEmptyMessage(int),sendMessage(Message)sendMessageAtTime(Message,
long)
,and sendMessageDelayed(Message,
long)
 methods.The post versionsallow you to enqueue Runnable objects to be called by the message queue when theyare received; the sendMessage versions
allow you to enqueue a Message objectcontaining
a bundle of data that will be processed by the Handler's handleMessage(Message) method(requiring
that you implement a subclass of Handler).

When posting or sendingto a Handler, you can either allow the item to be processed as soon as themessage queue is ready to do so, or specify a delay before it gets processed orabsolute time for it to
be processed. The latter two allow you to implementtimeouts, ticks, and other timing-based behavior.

When a process iscreated for your application, its main thread is dedicated to running a messagequeue that takes care of managing the top-level application objects(activities, broadcast receivers, etc)
and any windows they create. You cancreate your own threads, and communicate back with the main application threadthrough a Handler. This is done by calling the same post or sendMessage methods as before, but from your newthread. The
given Runnable or Message will then be scheduled in the Handler'smessage queue and processed when appropriate.

能够看到,Handler用来将Message插入到MessageQueue中,同一时候在适当的时候(即从MessageQueue中取出Message时),Handler也用来对消息进行处理。

Handler提供了多个函数用于将Message插入到MessageQueue中,我们以sendMessage为例,该函数定义例如以下:

492    /**
493 * Pushes a message onto the end of themessage queue after all pending messages
494 * before the current time. It will bereceived in {@link #handleMessage},
495 * in the thread attached to this handler.
496 *
497 * @return Returns true if the message wassuccessfully placed in to the
498 * message queue. Returns false onfailure, usually because the
499 * looper processing the message queue is exiting.
500 */
501 public final boolean sendMessage(Messagemsg)
502 {
503 return sendMessageDelayed(msg, 0);
504 }

实际上调用的是sendMessageDelayed函数,该函数定义例如以下:

549    /**
550 * Enqueue a message into the message queueafter all pending messages
551 * before (current time + delayMillis). Youwill receive it in
552 * {@link #handleMessage}, in the threadattached to this handler.
553 *
554 * @return Returns true if the message wassuccessfully placed in to the
555 * message queue. Returns false onfailure, usually because the
556 * looper processing the message queue is exiting. Note that a
557 * result of true does not mean the message will be processed -- if
558 * the looper is quit before the delivery time of the message
559 * occurs then the message will be dropped.
560 */
561 public final booleansendMessageDelayed(Message msg, long delayMillis)
562 {
563 if (delayMillis < 0) {
564 delayMillis = 0;
565 }
566 return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);
567 }

实际上调用的是sendMessageAtTime函数,该函数定义例如以下:

569    /**
570 * Enqueue a message into the message queueafter all pending messages
571 * before the absolute time (in milliseconds)<var>uptimeMillis</var>.
572 * <b>The time-base is {@linkandroid.os.SystemClock#uptimeMillis}.</b>
573 * You will receive it in {@link#handleMessage}, in the thread attached
574 * to this handler.
575 *
576 * @param uptimeMillis The absolute time atwhich the message should be
577 * delivered, using the
578 * {@link android.os.SystemClock#uptimeMillis} time-base.
579 *
580 * @return Returns true if the message wassuccessfully placed in to the
581 * message queue. Returns false onfailure, usually because the
582 * looper processing the message queue is exiting. Note that a
583 * result of true does not mean the message will be processed -- if
584 * the looper is quit before thedelivery time of the message
585 * occurs then the message will be dropped.
586 */
587 public boolean sendMessageAtTime(Messagemsg, long uptimeMillis) {
588 MessageQueue queue = mQueue;
589 if (queue == null) {
590 RuntimeException e = newRuntimeException(
591 this + "sendMessageAtTime() called with no mQueue");
592 Log.w("Looper",e.getMessage(), e);
593 return false;
594 }
595 return enqueueMessage(queue, msg,uptimeMillis);
596 }

实际上是调用enqueueMessage函数,将Message插入到MessageQueue中。该函数定义例如以下:

621    private boolean enqueueMessage(MessageQueuequeue, Message msg, long uptimeMillis) {
622 msg.target = this;
623 if (mAsynchronous) {
624 msg.setAsynchronous(true);
625 }
626 return queue.enqueueMessage(msg,uptimeMillis);
627 }

能够看到,622行,将Message.target设置为this,即当前Handler。这就解释了我们前面分析Looper时提出的问题:Looper从消息队列中取出了Message,然后调用Message.target.dispatchMessage函数,将消息发送到相应的处理函数,Message.target是什么时候被设置的?Message.target在使用Handler.sendMessage函数将消息发送到MessageQueue时,就被设置为当前Handler。

626行,调用MessageQueue.enqueueMessage将Message插入到MessageQueue中。

以上我们知道了如何通过Handler.sendMessage等函数将Message插入到MessageQueue中。而且Looper在循环从MessageQueue中取出一个Message后,会调用Message.target.dispatchMessage函数,即Message相应的Handler的dispatchMessage函数。以下我们就来看一下Handler.dispatchMessage函数,其定义例如以下:

90    /**
91 *Handle system messages here.
92 */
93 public void dispatchMessage(Message msg) {
94 if (msg.callback != null) {
95 handleCallback(msg);
96 } else {
97 if (mCallback != null) {
98 if(mCallback.handleMessage(msg)) {
99 return;
100 }
101 }
102 handleMessage(msg);
103 }
104 }

能够看到,假设指定了Message.callback,则调用Handler.handleCallback函数,其定义例如以下:

732    private static void handleCallback(Messagemessage) {
733 message.callback.run();
734 }

Message.callback是Runnable类的对象,733行,调用Message.callback.run函数開始运行该Runnable对象。

假设Message.callback为null,则调用Handler.handleMessage函数,这也是我们在应用程序中创建Handler对象时,须要重载实现的handleMessage函数。

须要注意的是,假设mCallback不为null,还须要先调用mCallback.handleMessage函数,假设该函数返回非0值,就不再调用Handler.handleMessage函数了。mCallback定义例如以下:

738    final Callback mCallback;

可见,它是Callback接口类型:

73    /**
74 *Callback interface you can use when instantiating a Handler to avoid
75 *having to implement your own subclass of Handler.
76 *
77 *@param msg A {@link android.os.Message Message} object
78 *@return True if no further handling is desired
79 */
80 public interface Callback {
81 public boolean handleMessage(Message msg);
82 }

分析到这里,我们就能理解Android的消息处理机制了。对于一个要处理消息的线程,它要调用Looper.prepare()创建当前线程的Looper对象,同一时候也创建了当前线程的MessageQueue对象,然后创建当前线程的Handler对象,重载Handler.handleMessage函数用于对消息进行处理,最后调用Looper.loop函数,循环从MessageQueue中取Message并分发处理。

在本文中,我们分析了一个普通线程怎样处理Message,而对于Android的UI线程,与普通线程处理Message的基本原理是一样的,但处理起来又有一定差别,下一篇文章中,我们将来分析UI线程是怎样处理Message的。

Android架构分析之Android消息处理机制(二)的更多相关文章

  1. Android架构分析之Android消息处理机制(一)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...

  2. Android架构分析之使用自定义硬件抽象层(HAL)模块

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本:2.3.7_r1 Linux内核版本:android-goldfish-2.6.29 在上一篇博 ...

  3. Solr4.8.0源码分析(19)之缓存机制(二)

    Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...

  4. Android架构分析之LOG模块

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本:2.3.7_r1 Linux内核版本:android-goldfish-2.6.29 Andro ...

  5. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  6. Android线程与异步消息处理机制

    在程序开发时,对于一些比较耗时的操作,我们通常会为其开辟一个单独的线程来执行,这样可以尽可能的减少用户等待的时间.在Android中,默认情况下,所有的操作都是在主线程中进行的,这个主线程负责管理与U ...

  7. Android 流量分析 tcpdump &amp; wireshark

    APP竞争已经白热化了,控制好自己Android应用的流量能够给用户一个良好的用户体验噢,给用户多一个不卸载的理由. Android 怎样进行流量分析?用好tcpdump & wireshar ...

  8. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  9. Android应用程序键盘(Keyboard)消息处理机制分析

    在Android系统中,键盘按键事件是由WindowManagerService服务来管理的,然后再以消息的形 式来分发给应用程序处理,不过和普通消息不一样,它是由硬件中断触发的:在上一篇文章< ...

随机推荐

  1. Storm技术结合

    http://pan.baidu.com/s/1mhzj5XI?qq-pf-to=pcqq.group#path=%252F

  2. eclipse, Log4j配置(真心的详细~)

    转自: http://www.cnblogs.com/alipayhutu/archive/2012/06/21/2558249.html a). 新建Java Project>>新建pa ...

  3. 使用NSURLSession获取网络数据和下载文件

    使用NSURLSession获取网络数据 使用NSURLSession下载文件

  4. Android中改变dialog的显示的位置和大小

    private void setDialogSize(Dialog dg) { Window dialogWindow = dg.getWindow(); WindowManager.LayoutPa ...

  5. android:process为耗资源操作指定一个新进程

    当有一些耗费内存比较多的操作时,可以通过android:process指定一个新的进程.保证程序运行. 例如: 一个后台长期运行的service: <service android:name=& ...

  6. [LeetCode#272] Closest Binary Search Tree Value II

    Problem: Given a non-empty binary search tree and a target value, find k values in the BST that are ...

  7. Running an etcd cluster on localhost

    Purpose Run a cluster on localhost while investigating etcd Use a static cluster (So we have no exte ...

  8. bzoj3165 1568

    1568是3165的弱化版,发的代码是3165的这道题完全没想出来,是看wyl大神的题解http://hi.baidu.com/wyl8899/item/2deafd3a376ef2d46d15e99 ...

  9. Wordpress Jigoshop插件路径泄露漏洞

    漏洞名称: Wordpress Jigoshop插件路径泄露漏洞 CNNVD编号: CNNVD-201311-109 发布时间: 2013-11-12 更新时间: 2013-11-12 危害等级:   ...

  10. POJ 3020 Antenna Placement 解题报告

    题意就不说了,求二部图最大匹配. 问题是怎么建图…… 给定的条件中,h<40,w<10,所以笔者直接默认所有情况的地图都是40*10,当然,超出范围的便是空城o. 然后给城市编号.一个城市 ...