您的位置:首页 > 移动开发 > Android开发

Android消息机制

2016-04-27 16:25 309 查看
Android的消息机制主要是指Handler的运行方式。

对于Android开发者而言,Handler并不陌生,它常常被用来向主线程post消息,更新UI等操作。日常开发中只需和Handler类交互即可,然而,稍微深入了解Handler运行机制可以让开发者更加熟悉Android系统消息机制,做到运用自如。

Handler的运行需要MessageQueue和Looper的支撑。MessageQueue保存消息,提供消息队列的实现;Looper实现线程循环处理消息和分发;其他线程通过Handler对象可以向Looper线程发送消息(该消息最终由创建Handler的线程负责处理),Handler MessageQueue和Looper三者工作关系大致如下:



1.MessageQueue

该类是消息队列实现类,消息实体由Message类实现。在MessageQueue类中维护着一个Message链表(虽然取名为队列,其实现是单向链表),其核心操作是向消息队列添加消息以及获取消息,分别对应于方法enqueueMessage()和next()。

enqueueMessage方法原型如下:

boolean enqueueMessage(Message msg, long when) {
//单向链表同步插入
}


next方法即是从链表中获取一条消息并返回给调用者,同时将该条消息从链表中删除;

MessageQueue的逻辑并不复杂,需要了解的基本操作就是插入和获取,很清晰。

2.Looper

Looper类的核心职能是循环读取消息队列中的消息,并分发出去。

Looper构建

Looper类核心变量:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

final MessageQueue mQueue;
final Thread mThread;


其中sThreadLocal 是线程私有变量,为每个创建Looper的线程保存其创建的Looper对象;

mQueue是消息队列;

mThread是创建Looper的线程;

在Looper对象的构造方法中会初始化消息队列:

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


从上面代码片段可知Looper的构造函数是私有的,因此,Looper对象的构建并不是手动创建的。从源码可以得知,通过静态方法 prepare() 可以为当前线程创建Looper对象,同时在Looper的创建中又会创建消息队列。因此,在使用时只需在当前线程内调用Looper.prepare(),即可完成Looper对象和消息队列的构建,示例如下:

new Thread("#SubThread"){
@Override
public void run() {
Looper.prepare();
}
}.start();


这样即可给线程”#SubThread”构建Looper和MessageQueue(当然仅仅是给Handler的使用创建必须的消息队列和循环机制,仅这样不能构建消息机制)。Looper还为主线程单独提供prepareMainLooper()方法(内部调用prepare方法),并保存主线程的Looper对象且提供获取主线程Looper的方法getMainLooper()。

开启消息循环

构建Looper之后必须调用loop()方法,手动开启消息循环。为线程构建消息队列的完整步骤如下:

new Thread("#SubThread"){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();


分析源码可知,loop方法是一个死循环,不断地从消息队列中获取消息(如果队列暂时没有消息,则阻塞直至有线程向消息队列添加消息为止),并将消息分发。

至此 线程”#SubThread”已构建消息队列,并开启消息循环。其它线程通过handler即可向线程”#SubThread”发送消息。

3.Handler

Handler类是消息机制中最常被开发者使用。常见的情况是在UI线程(主线程)中使用Handler,子线程通过Handler对象完成更新UI等操作,而无须接触MessageQueue和Looper。究其原因是主线程在启动过程中已完成MessageQueue和Looper的创建,以下是ActivityThread类main方法的代码片段:

public static void main(String[] args) {
//省略多行代码
Looper.prepareMainLooper();

//省略多行代码

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}


可见,在主线程启动过程中,已经构建消息队列并开启消息循环,因此,在主线程中,我们可以方便的使用Handler,而无须关心其对应得Looper和MessageQueue。但是,为了更好理解消息机制,了解Handler是如何与Looper和MessageQueue协同工作还是有必要的。

Handler的创建

如上方式
Handler handler = new Handler();
创建Handler对象最终会调用下边的构造方法:

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}


代码逻辑很清晰,如果是在没有构建Looper的线程内调用此构造方法会抛出运行时异常,并提示“不可以在未调用Looper.prepare()方法的线程内创建Handler对象”;创建过程中handler会保存Looper中消息队列的引用,便于后续的消息添加。

通过Handler发送消息

通过调用Handler类提供的sendMessage系列方法可以发送消息,不同类型的sendMessage方法发送的消息内容设置稍有不同,以一条空消息为例,如下调用即可:

handler.sendEmptyMessage(MSG_TEST);


方法经过几次传递最终会调用enqueueMessage方法,该方法是Handler发送消息的实际操作部分,代码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}


该方法其实也不是真正干活的,它调用MessageQueue的enqueueMessage方法向消息队列中添加一条消息。

因此通过Handler发送消息的逻辑最终归结为向MessageQueue消息链表内添加一条消息。

发送的消息如何传递至handleMessage()方法

一般在构建Handler对象时,我们会重写handleMessage方法,在该方法内处理各个不同类别的信息,典型逻辑如下:

Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_ONE:
handleMsgOne();
break;
case MSG_TWO:
handleMsgTwo();
break;
default:
break;
}
}
};


那么经由handler 调用sendMessage的消息是如何被传递至handleMessage方法内的呢?

上一段,我们讲到sendMessage最终是在消息队列中添加一条消息记录,而是谁在不断读取并分发消息队列中的消息呢?没错就是Looper。Looper所在的线程调用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;

//省略多行

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

//省略多行

msg.target.dispatchMessage(msg);

//省略多行

msg.recycleUnchecked();
}
}


loop方法的逻辑也比较清晰:调用MessageQueue的next()方法获取一条消息,调用消息对象msg的target字段的dispatchMessage方法分发消息;

那么,分发消息应该就是由,这个msg所对应的target来处理了。

Message类中target字段定义
/*package*/ Handler target;


可知,target是一个Handler对象,而在sendMessage的过程中的最终函数enqueueMessage(代码见上文)中有关键的一句:

msg.target = this;


即将msg的target对象指向了发送msg的handler,因此,dispatchMessage最终还是由发送msg的Handler对象处理。

Handler的dispatchMessage方法如下:

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


终于,在这里调用了hanleMessage()方法,把消息处理的最终实体回调给Handler的定义者(hanleMessage()默认为空实现)。

看到这里,相信读者对Android的消息机制有一定的了解了。

那么有同学可能会问了,Handler内先通过sendMessage添加消息,再经由Looper回调handleMessage()方法,兜一圈不累吗?

Android系统的设计者们估计会说,累但很有必要。细心的读者会发现,调用sendMessage 和回调handleMessage的线程是不同的。sendMessage可以在任何子线程内通过handler对象发送,而回调handleMessage的线程是当初创建Handler ,Looper和MessageQueue的线程。这也就是为什么我们常常在主线程内创建Handler对象,重写相应的handleMessage()逻辑并且可以在此更新UI,而各子线程内一般只能通过这个Handler对象发送消息。

Handler,MessageQueue和Looper三者的配合构成了Android系统的消息机制,提供了一种线程通信实现方式,它在Android FrameWork 可以经常看到,使用简单(尤其是也主要是在UI线程和子线程通信的情况中),希望大家通过这篇文章可以对它有更好的了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: