android|源码分析-Android中的消息机制详解

本人原创,转载请注明出处哈:http://blog.csdn.net/tyhj_sf/article/details/51105453
1 引言 个人认为,不亲自阅读和熟悉Android的源码,不了解Android源码背后的实现机理,就不能真正成为Android开发的高手。前两天为了了解android的消息处理机制,我阅读和分析了Looper,Handler,MessageQueue,Message这几个类的源码,不得不对Google大牛们膜拜下,现在把学习的笔记拿出来特与大家分享。
2 消息机制的基本原理 从大的方面讲,不光是Android平台,各种平台的消息机制的原理基本上都是相近的,其中用到的主要概念大概有:
1)消息发送者;
2)消息队列;
3)消息处理循环。
示意图如下:
android|源码分析-Android中的消息机制详解
文章图片

图中表达的基本意思是,消息发送者通过某种方式,将消息发送到某个消息队列里,同时还有一个消息处理循环,不断从消息队列里摘取消息,并进一步解析处理。
在Android平台上,把上图的右边部分包装成了一个Looper类,这个类的内部具有对应的消息队列(MessageQueue mQueue)和loop函数。
但是Looper只是个简单的类而已,它虽然提供了循环处理方面的成员函数loop(),却不能自己凭空地运行起来,而只能寄身于某个真实的线程。而且,每个线程最多只能运作一个Looper对象,这一点应该很容易理解。
Android平台上另一个关键类是Handler。当消息循环在其寄身的线程里正式运作后,外界就是通过Handler向消息循环发出事件的。我们再画一张示意图如下:
android|源码分析-Android中的消息机制详解
文章图片

在Android应用中为线程创建消息处理程序的常规用法如下示例代码:

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(); } }

3 Android消息机制相关的核心类 android的消息处理有四个核心类:Looper,Handler和MessageQueue(消息队列),Message。其中MessageQueue被封装到Looper里面了,我们不会直接与MessageQueue打交道。下面一一介绍:
3.1 Looper类
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:
public class LooperThread extends Thread { @Override public void run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // ...其他处理,如实例化handler// 开始循环处理消息队列 Looper.loop(); } }

【android|源码分析-Android中的消息机制详解】让我们看看与Looper相关的这两行代码各自做了什么。
1)Looper.prepare()的源码:
public class Looper { // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象 private static final ThreadLocal sThreadLocal = new ThreadLocal(); // Looper内的消息队列 final MessageQueue mQueue; // 当前线程 Thread mThread; // 。。。其他属性// 每个Looper对象中有它的消息队列,和它所属的线程 private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }// 我们调用该方法会在调用线程的TLS中创建Looper对象 public static final void prepare() { if (sThreadLocal.get() != null) { // 试图在有Looper的线程中再次创建Looper将抛出异常 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } // 其他方法 }

通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象保存到ThreadLocal中去。如果你还不清楚什么是ThreadLocal,可以去看看它源码,这里不对它进行细说,我们只需要知道是每一个线程对象都有一个ThreadLocal类型的成员变量,而这段源码里的sThreadLocal就是当前线程的ThreadLocal类型的成员变量的引用。
2)Looper.loop()方法的源码:
当调用loop方法后,Looper线程就开始真正工作了,loop()方法内部的For(;;)死循环不断从自己的MessageQueue中取出队头的消息(也叫任务)执行。其源码分析如下:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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.recycle(); } }

除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如:
prepareMainLooper(),如果是主线程,调用此方法为主线程准备一个looper,主线程的looper有应用程序初始化;
Looper.myLooper()得到当前线程looper对象;
getThread()得到looper对象所属线程;
quit()方法结束looper循环。
到此为止,你应该对Looper有了基本的了解,总结几点:
1.每个线程有且最多只能有一个Looper对象,它被添加到ThreadLocal中去。每个线程都有一个ThreadLocal类型成员变量。
2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
3.Looper使一个线程变成Looper线程。
3.2 Message类
Message类中含有一个Message类型的成员变量next。学过数据结构就可立刻知道,这是链表节点的结构,因此MessageQueue实际上维护的是一个Message链表。Message部分源码:
public final class Message implements Parcelable {public int what; public int arg1; public int arg2; public Object obj; public Messenger replyTo; /*package*/ static final int FLAG_IN_USE = 1 << 0; /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; /** Flags to clear in the copyFrom method */ /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; /*package*/ int flags; /*package*/ long when; /*package*/ Bundle data; /*package*/ Handler target; /*package*/ Runnable callback; // sometimes we store linked lists of these things /*package*/ Message next; ·················· ··················

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,这里不做总结了。但是有这么几点做开发时需要注意(有待补充):
1.尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
2.如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
3.擅用message.what来标识信息,以便用不同方式处理message。
3.3 Handler类
3.3.1 什么是handler? handler扮演了往MessageQueue上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以通过它的几个有参构造方法来指定looper的。
下面我们就可以为之前的LooperThread类加入Handler:
public class LooperThread extends Thread { private Handler handler1; private Handler handler2; @Override public void run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // 实例化两个handler handler1 = new Handler(); handler2 = new Handler(); // 开始循环处理消息队列 Looper.loop(); } }

可以看到,一个线程可以有多个Handler,但是只能有一个Looper!
3.3.2 Handler发送消息 有了handler之后,我们就可以使用
post(Runnable),
postAtTime(Runnable, long),
postDelayed(Runnable, long),
sendEmptyMessage(int),
sendMessage(Message),
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
这些方法向消息队列上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,见源码:
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行 public final boolean post(Runnable r) { // 注意getPostMessage(r)将runnable封装成message returnsendMessageDelayed(getPostMessage(r), 0); }private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); //得到空的message m.callback = r; //将runnable设为message的callback, return m; }public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; // message的target必须设为该handler! sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }

其他方法就不罗列了,总之通过handler发出的message有如下特点:
1.message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码
msg.target.dispatchMessage(msg);

2.post发出的message,其callback为Runnable对象
3.3.3 Handler处理消息 消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,见源码:
// 处理消息,该方法由looper调用 public void dispatchMessage(Message msg) { if (msg.callback != null) { // 如果message设置了callback,即runnable消息,处理callback! handleCallback(msg); } else { // 如果handler本身设置了callback,则执行callback if (mCallback != null) { /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */ if (mCallback.handleMessage(msg)) { return; } } // 如果message没有callback,则调用handler的钩子方法handleMessage handleMessage(msg); } }// 处理runnable消息 private final void handleCallback(Message message) { message.callback.run(); //直接调用run方法! } // 由子类实现的钩子方法 public void handleMessage(Message msg) { }

可以看到,除了handleMessage(Message msg)和Runnable对象的run( )方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。这正是handler API设计的精妙之处!
3.3.4 Handler的作用 1.只要线程A的handler传进另一个线程B中,就可以在线程B中向线程B发送消息,这些消息会被添加到关联的MessageQueue上。
android|源码分析-Android中的消息机制详解
文章图片

2.handler是在它关联的线程中处理消息的。
handler创建时,成员变量包含了一个looper,而looper关联到一个线程的。因此可以判断handler关联到哪个线程。
这就解决了android不能在其他非主线程中更新UI的问题。
Activity是在主线程运行的,在Activity中创建handler,那么这个handler就属于主线程,我们可以将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知Activity更新UI。
过程如下图所示:
android|源码分析-Android中的消息机制详解
文章图片

下面给出一个在worker thread通过发消息更新UI的例子(代码仅供参考):
public class TestDriverActivity extends Activity { private TextView textview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textview = (TextView) findViewById(R.id.textview); // 创建并启动工作线程 Thread workerThread = new Thread(new SampleTask(new MyHandler())); workerThread.start(); }public void appendText(String msg) { textview.setText(textview.getText() + "\n" + msg); }class MyHandler extends Handler { @Override public void handleMessage(Message msg) { String result = msg.getData().getString("message"); // 更新UI appendText(result); } } }

public class SampleTask implements Runnable { private static final String TAG = SampleTask.class.getSimpleName(); Handler handler; public SampleTask(Handler handler) { super(); this.handler = handler; }@Override public void run() { try {// 模拟执行某项任务,下载等 Thread.sleep(5000); // 任务完成后通知activity更新UI Message msg = prepareMessage("task completed!"); // message将被添加到主线程的MQ中 handler.sendMessage(msg); } catch (InterruptedException e) { Log.d(TAG, "interrupted!"); }}private Message prepareMessage(String str) { Message result = handler.obtainMessage(); Bundle data = https://www.it610.com/article/new Bundle(); data.putString("message", str); result.setData(data); return result; }}

4 分析源码后对几个疑惑的回答
  1. 问:消息机制相关的类几个类?
    答:Thread、 Handler、Looper、MessageQueue、Message
  2. 问:Thread如何获得Looper?
    答:对于Main Thread,调用Looper.getMainLooper( ),因为Looper已由应用创建。对于worker Thread,调用Looper.prepare( )创建一个关联的looper。
  3. 问:handler如何从消息队列中取消息并处理?
    答:(1)handler持有Looper类型变量mLooper,handler在初始化时,将相关联的线程的Looper赋值给了mLooper。(2)handler持有MessageQueue类型变量mQueue,而mQueue通过赋值语句(mQueue=Looper.mQueue;)实例化。
  4. 问:Message怎样被传递给Handler.handleMessage(msg)方法的?
    答:在Looper.loop()方法中,for死循环中调用了Handler.dispatchMessage(msg)方法(该方法中调用了handleMessage(msg)钩子方法)直到消息队列为空退出循环。
  5. 问:一个线程的handler怎么知道另外的线程发送了msg消息并从消息队列取消息处理呢?
    答:在调用handler.sendMessage(msg)发送了msg消息时,sendMessage(msg)方法内部调用了enqueueMessage()方法,enqueueMessage()内部则调用了C/C++代码接口方法nativeWake( ),在这里我们不在向下研究C++层的代码。通过查资料得知:我们在构造Looper对象时,其内部创建了一个监听机制来监听消息入队,一旦有消息入消息队列进进行消息处理。

    推荐阅读