源码分析-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中的消息机制详解_第1张图片
图中表达的基本意思是,消息发送者通过某种方式,将消息发送到某个消息队列里,同时还有一个消息处理循环,不断从消息队列里摘取消息,并进一步解析处理。

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

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

让我们看看与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
       return  sendMessageDelayed(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中的消息机制详解_第3张图片

2.handler是在它关联的线程中处理消息的。

handler创建时,成员变量包含了一个looper,而looper关联到一个线程的。因此可以判断handler关联到哪个线程。

这就解决了android不能在其他非主线程中更新UI的问题。

Activity是在主线程运行的,在Activity中创建handler,那么这个handler就属于主线程,我们可以将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知Activity更新UI。
过程如下图所示:
源码分析-Android中的消息机制详解_第4张图片

下面给出一个在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 = 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对象时,其内部创建了一个监听机制来监听消息入队,一旦有消息入消息队列进进行消息处理。

你可能感兴趣的:(android,消息机制,handler,Looper,线程间通信)