FROM:http://blog.sina.com.cn/s/blog_49f62c350101gts9.html
正如其他GUI应用程序一样,Android应用程序也是消息(事件)驱动的。这种消息的传递必须依赖于应用框架提供的消息机制。Android本身提供了2种消息机制:
· 组件间消息传递 --- Intent
· 线程间消息传递 --- Message
本文主要讨论Android线程间消息传递机制及其应用。
1.Android Message机制
所谓消息机制,就是实现消息传递、消息分发、消息处理的一套程序逻辑,一般属于应用程序框架的功能范围。应用程序开发者可以利用应用程序框架提供的消息API来开发应用。典型的消息API有SendMessage(), PostMessage(), HandleMessage(),DispatchMessage(). 同理,Android Message机制也提供了类似API,其内部实现机制也跟其他平台一样,基于消息队列。但是Android Message都是异步处理的。
1.1 消息传递
Android Message机制的消息队列采用单链表结构。
其中mMessages是头指针,m1, m2, m3..是Message节点。每个Android消息都有个相对开机时间的过期时间,消息队列是基于这个过期时间先后顺序排序的,需要尽快处理的消息排在前面。所以其插入算法是:
参考代码:\frameworks\base\core\java\android\os\MessageQueue.java@enqueueMessage()
1.2 消息分发
Android 设计了一个死循环来负责消息的分发, 它不停的从消息队列获取过期的消息,一旦取得,就调用该message对应的handler去处理。由于消息队列是基于消息过期时间排序的。所以该分发函数的实现逻辑非常简单,每次只是判断第1个节点对应的消息是否过期,如果过期,则从链表取走(删除),并分发给该消息对应的handler去处理,处理之后回收该消息结构;如果没有过期的消息,则block自己一段时间(可能消息队列里没有消息了)。 主要实现逻辑如下:
参考代码:\frameworks\base\core\java\android\os\Looper.java@loop()
\frameworks\base\core\java\android\os\MessageQueue.java@next()
其中msg.target是预先设置的消息处理handler。每个消息都一个对应的handler,该handler负责分发或默认处理消息。msg.recycle()负责把用完的消息结构回收,以便复用该结构。
1.3 消息处理
跟其他平台的消息机制一样,Android也提供了接口供用户注册/创建自己的消息处理方法,同时也支持消息携带特定的处理函数,因此Android的消息处理更加灵活。
基本逻辑如下:
参考代码:\frameworks\base\core\java\android\os\Handler.java@dispatchMessage()
2. Message相关的类
Android Message机制中用到以下几个重要类:
Looper, MessageQueue, Message, Handler
它们之间的关系是
2.1 Message类
该类负责Message对象的创建/回收/销毁,对应一个特定的消息。维护一个可用消息链表用于回收用完的消息结构(最大size为10)。
参考代码:frameworks\base\core\java\android\os\Message.java
2.2 MessageQueue类
该类实现Message队列,它是个单链表结构,提供链表的插入/删除/检索操作。外部可以利用特定函数对消息进行链表操作。它总是被Looper创建,与Looper是一对一的关系。
参考代码:\frameworks\base\core\java\android\os\MessageQueue.java
2.3 Looper类
该类实现消息的定时分发,属于消息机制的核心机构。它负责轮询消息队列,找出过期的消息并分发特定handler进行处理。因此一个Looper必须有一个MessageQueue。
参考代码:\frameworks\base\core\java\android\os\Looper.java
2.4 Handler类
该类实现消息的发送以及处理,负责发送用户消息以及调用用户注册的callback或接口进行消息处理。因此每个消息肯定有一个对应的handler,否则消息无法被发送/处理。
参考代码:\frameworks\base\core\java\android\os\Handler.java
2.5 ThreadLocal类
该类存储Thread相关的数据。此处用于存储与Thread关联的Looper实例。这样每个Thread都可以有自己的消息队列以及消息处理方式。
2.6 Callback类
该类是一个接口,用户在创建Handler实例时可以传入该interface实例,这样一旦消息到来,handler会调用该接口来处理。
2.7 Runnable类
该类也是一个接口,用户可以向特定的Runnable发送消息,一旦成功,该Runnable会被调用。一旦某个消息指定了自己的callback(Runnable), 那么该消息将交给它的callback去处理,不会交给Handler的Callback接口或Handler的handleMessage()处理。
在实际应用中,它们有如下的调用关系:
3. 与Thread的关系
· 1个Android 应用程序默认只有1个Process,但可以有多个Thread
· 1个Android 应用程序对应1个ActivityThread实例, 它就是应用的主线程(UI 线程)
· 系统默认为主线程创建了1个消息循环(Looper),并启动它开始接收消息
· 其他线程默认没有Looper,所以没有 MessageQueue,不能接收Message, 如果需要接收Message, 则需要通过Looper.prepare()创建一个MessageQueue
· 1个线程只能有1个Looper, 也就是说只能有1个MessageQueue
· 1个线程可以有多个Handler, 多个Runnable, 多个Callback
4. Message机制的应用
4.1 子线程向主线程发送消息
主线程中所做的操作最好耗时非常短,一般建议创建子线程去完成比较费时的工作(访问网络,下载数据,查询数据库等), 以免主线程阻塞而发生ANR异常。但子线程通常不能更新UI,异常android.view.ViewRoot$CalledFromWrongThreadException:Onlythe original thread that created a view hierarchy can touch itsviews。所以子线程与主线程需要通过Message来沟通信息。这个场景很常见。实现逻辑如下:
主线程创建Handler实例
privateHandler mHandler = new Handler(new Handler.Callback() {
public boolean handleMessage(Message msg){
switch (msg.what) {
case UPDATE_UI:
{
//updateUI
break;
}
default:
break;
}});
子线程利用主线程的Handler向主线程发送消息:
MessagenotifyMsg = mHandler.obtainMessage(UPDATE_UI, 60, 100, null);
mHandler.sendMessage(notifyMsg) ;
4.2 主线程向子线程发送消息
子线程创建自己的Handler实例并建立消息Looper
class subThread extends Thread{
public void run() {
Looper.prepare();
mSubThreadHandler= new Handler(){
public void handleMessage(Message msg) {
Log.e(sTag, (String)msg.obj);
}
};
Looper.loop();
}
}
主线程利用子线程的Handler向子线程发送消息:
Message cmdMsg =mSubThreadHandler.obtainMessage(CMD_STOP, 0, 0, null);mSubThreadHandler.sendMessage(cmdMsg);
4.3 向Runnable发送消息
可以通过Handler直接发送消息来运行某个Runnable。这种方式更像用消息机制实现一个Timer来完成某个工作。Runnable运行在Handler所在的线程中,单次执行。
mHandler.post(new Runnable(){
public void run() {
//do something
}
});
由于Thread是Runnable的子类,也可以这样使用:
Thread myThread = new Thread(){
public void run() {
//do something
}
};
mHandler.post(myThread);
注意此时并不会启动一个线程,跟上面的runnable方式一样,run()会运行在Handler所在的线程中,本质上就等同于Handler调用myThread.run(), 就是一个普通调用,所以不会新开一个线程。