Android中的消息队列与消息循环:认识Handler、Looper

    学习android到今天也将近3个月了,虽然各种界面各种组件一直在欢快的用着,但有一天忽然发现,一些简单的东西去不是很理解:比如Bundle和Handler。于是就抽空好好阅读了下adk文档。现在也算是理解一二了,技术需要分享,于是乎花了半天的时间整理了这篇文章。

1、Bundle(打酱油的)

这应该是任何一个学习android的人创建的第一个hello world程序都会遇到的一个类,它是activity的oncreate的入参,但由于做东西的时候很少会遇到它,也就一直没在意。

这个类很简单,先听听官方的解释:“A mapping from String values to various Parcelable types.”

Soga,原来就是一个String – Object的映射表,同时又是支持序列化的。

再看代码:“final class Bundle implements Parcelable, Cloneable ”。实现了两个接口,前者是序列化接口,后置是浅拷贝和深拷贝的接口,具体实现就不再说了,这个不是重点。

总之一句话,Bundle就是为了实现数据传输而生的,比如activity之间。通常Bundle会附在intent里面intent.putExtra()。

好了,打住,进入下一个议题:消息循环!

2、Windows消息循环

做过客户端的肯定都会接触到一个概念:UI线程。说白了,UI线程和普通工作线程的区别就在于消息循环。消息循环就是用来处理分发相关线程消息队列中的消息,Android和Windows都是如此。作为一名曾经的windows开发人员,研究的第一个点也是系统对UI的消息处理流程,不然干活都没底气。

int _WinMain(args ...)
{
    //WND definition ... ...
     while (GetMessage(&msg, NULL, 0, 0))
     {            
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
   }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
{
    switch(message)
    {
        ... ...
    }
}


上面就是经过简化后的一个最基本的windows的创建窗口的过程。其中main函数里面的while就是一个消息循环,来不断从消息队列里面读取消息并dispatch消息。dispatch的消息均会进入到消息处理回调函数WndProc中完成消息的处理。这个程序的main线程也就是通常说的UI线程。假如我们另起一个线程包含了上面的while循环,则那个线程也照样可以创建窗口什么的,那么它也是一个UI线程。

如果对win32的ui感兴趣,可以参考http://blog.csdn.net/cheneywong/article/details/8847810

上面说了这么些的windows相关技术,其实就是想描述下窗口处理的基本原理,因为有些概念在windows上更易懂写,这样再看android的时候容易理解些。


3、Android的Looper

官方解释:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

很明白,looper就是一个用来处理消息的消息循环。默认的Thread也是不具有处理消息的能力,需要添加Looper来实现。说白了,android中的UI线程也就是带有一个Looper的Thread,用户所有的操作产生的消息均通过Looper来分发处理(这句没有看代码验证,但应该也是八九不离十了)。

和上一节比较我们发现好像少了一点东西 — 消息处理回调函数WndProc。

Most interaction with a message loop is through the Handler class.”

这句话也引出了Looper和Hanlder的关系了。一个消息循环分发消息,一个消息处理。如下一段实例代码,演示了线程中添加消息循环并处理的过程:

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

看了这段代码,我就立刻产生了一个疑问,windows中的wndproc是需要注册才能进入处理的,那上段代码handler没有注册却能接收并处理消息!!

4、Android的Handler

官方解释:A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
 简单翻译:Handler允许你向线程的MessageQueue发送消息,同时还能够处理该消息队列里的Message和Runnable。每个handler只能关联一个线程,并在创建的时候和线程绑定。


原来创建Hanlder的时候,它会自己绑定到线程和消息队列上,这就解释了上一节的疑问了。


另外值得注意的一条规则:每个Handler只会和一个Thread绑定,即创建它的Thread。后面会讲到这条规则的用处。

这里面又引入了两个概念:Message和向消息队列发送消息。

Message

消息体,查看源码大致包含了如下几个核心成员:

Bundle date ;        // 消息体包含的数据

Handler  target;    // 消息对应的“target”, 即由谁来处理

Runable callback; // 消息处理的回调

Message next;      // 下一条消息

5、再看Looper

Looper的结构相对简单,主要有以下两个成员

{

    final MessageQueue mQueue;

    final Thread mThread;

}

通过成员变量,我们可以看出一个消息循环Looper均和一个Thread相关联,同时还会附带一个消息队列MessageQueue。

另外,还有两个静态成员:

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

该对象可以理解成管理Thread和Looper的关系就好了。

private static Looper sMainLooper; 

主线程的Looper。这样,任何就可以在任何线程获取到主线程(UI线程)的消息循环体了,进而可以向UI线程发送消息。

最后,我们再看下之前代码中出现的Looper.prepare()和Looper.loop()。这两个函数均是静态函数


prepare()

通过ThreadLocal创建Looper。


loop():

简化后的伪代码大致如下:

public static void loop() {
    loop = getThisThreadLooper();
    queue = loop.mQueue;
    for(;;){
        msg = queue.next();
        // 分发消息
        msg.target.dispatchMessage(msg);
        msg.recycle();
    }
}

原来loop函数也就是一个无线循环,不断的从消息队列里面取出消息并交给Message中的目标Handler处理。好了,这就清晰了,消息循环原来如此简单。


附加:prepareMainLooper

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

该函数顾名思义,就是来准备主线程(UI线程)的消息循环,代码的注释中也说明了:该函数是初始化application的主looper。该函数有android系统来调用,我们不应该自己来调用,不然会抛出异常。


6、再看Handler

上一节中出现了一个函数Handler.dispatchMessage(Message).  下面我们也会重点分析该函数的。

{
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
}

Handler中的成员主要有上面3个,消息队列、消息循环和回调Callback。


(1)初始化

    获取创建自己的线程的消息循环以及消息队列:mLooper = Looper.myLooper();

    另外用户可以为Handler指定一个Callback 。

(2)Callback 接口和handleMessage成员方法

    Handler内部定义了如下接口:

public interface Callback {
        public boolean handleMessage(Message msg);        // 返回值意思就是callback处理完之后是否还需要继续处理,true就是已经处理过了,不需要再进一步处理
    }

    另外值得注意的是Handler自己还有一个成员函数

    public void handleMessage(Message msg) {
    }

    擦,你这是要闹哪样!!

    查看下代码的注释:

    Callback:”Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.“

    handleMessage成员函数:”Subclasses must implement this to receive messages.“

    这样就水落石出了:成员函数主要用在用户自己继承Handler实现时来处理message的,而Callback接口则是为了用户直接创建Handler时能够处理message用的,而不必通过子类实现。


(3)dispatchMessage

    Message中有一个Runnable的Callback成员,现在Handler中也有一个Callback,另外Handler还有一个成员函数handleMessage(Message),哇塞,晕掉了有木有,这三个必须要有一个执行次序。代码如下:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            msg.callback.run();
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
}

即优先级为:1. 消息体中的Runnable; 2. 如果为空则执行创建handler时指定的Callback;3. 如果没指定或者返回值为false的话就执行handler的handleMessage方法。

至此,android中线程的消息循环和消息队列就说明的差不多了。

Handler的Send/Post Mesage

在SDK文档中的描述Handler,其有两大主要用处:其一,安排message和runnable在某一时间点执行;其二,向其他线程的消息队列中发送消息(比如UI主线程)。

其中第二项功能就是我们通常用来在异步线程和UI线程打交道的方法了。这主要通过Handler提供的send和post系列方法完成。send对应message,post对应Runnable。

如下:

post(Runnable)

postAtTime(Runnable, long)

postDelayed(Runnable, long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message, long)

sendMessageDelayed(Message, long)

每个方法的具体含义请自行查看SDK文档。这里还是要解释下post的实现:

public final boolean post(Runnable r)
 {
       return  sendMessageDelayed(getPostMessage(r), 0);
 }
public final boolean sendMessage(Message msg)
{
        return sendMessageDelayed(msg, 0);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            // exception
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
 }

原来post也是将Runnable转换成一个message(还记得Message有一个Runnable成员吗),然后调用send的delay方法。最终的sendMessage也只是将消息压入消息队列中。

注意:这里面的post和send含义和Windows应用程序的post和send的含义是不同的。

(1)线程内

    上面的分析,我们就可以在线程内向handler发送自定义message或post一个runnable。

(2)线程间

    之前我们说过:一个Handler只能和创建它的线程绑定在一起,这样我们就可以在主线程创建一个Handler,然后再工作线程中向这个Handler 进行send 或post操作,这样创建的message均会加入到UI线程的消息队列中并被执行,这也就实现了UI线程和普通工作线程的通信。如下一个代码示例:

class MyActivity{
	Handler handler;
	
	... ...
	void init(){
		handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				//处理消息:将网络获取的数据在UI上表现出来
				... ...
			}
		};
	}
	
	void getDataFormNet(){
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				//网络操作
				... ...
				handler.sendMessage(new Message());
			}
		});
		
		thread.start();
	}
}

OVER了,学习时间不长,如有错误请指正~~





    原文作者:移动开发
    原文地址: https://my.oschina.net/cheneywangc/blog/202469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞