您的位置:首页 > 产品设计 > UI/UE

Handler的实现原理及其与Message、MessageQueue关系详解

2015-08-13 15:39 941 查看
       在Android中为我们提供了一个主线程和子线程之间的通信机制,这种机制就是利用Handler、Message、Looper和MessageQueue来彼此联系起来的。那么为什么要提供这么一种机制呢?因为Android是单线程模式的,所谓单线程模式就是说,在app启动时就会创建一个对应的唯一的一个主线程(ActivityThread实例),这个实例维护着界面ui的更新、用户的各种响应事件等。这些更新只能放在主线程中执行(典型的例子就是在Framework中会检查主线程中是否有网络请求的操作,如果有就会直接抛出异常),主线程并不安全,并且耗时的操作必须放在子线程中去执行,这里我不再讨论线程不安全、耗时操作不能放在主线程中执行的原因,前面的文章介绍过。基于这个原因,Framework引入了这么一种主线程和子线程通信的机制,Handler、Message、Looper和MessageQueue彼此协作完成通信。

第一部分  主线程与子线程之间的通信实例

       本文分两部分介绍,第一部分先展示如何使用这种机制来完成主线程和子线程的通信,第二部分介绍其实现的原理源码分析。

1、常规用法,在子线程中直接使用handler.sendMessage()方法

//创建Handler对象,并重写其处理方法
mUIhandler = new UIHandler() {
@Override
public void handleMessage(Message msg) {
// 处理请求后的任务
}
};
// 第一种方式
new Thread(new Runnable() {
@Override
public void run() {
// 这里处理耗时、网络操作,
// 下面处理完后发送消息给Handler来处理
Message msg = mUIhandler.obtainMessage();
msg.what = 0;
// msg.obj = value;
mUIhandler.sendMessage(msg);
}
}).start();
        上面的使用方法很简单,就是先创建一个Handler对象,然后重写其handleMessage()方法,接下来开启子线程,并执行run()中的请求业务,之后再获取Message对象,然后发送出去给Handler的handleMessage()处理。

2.指定时间点发送消息

long tm = SystemClock.uptimeMillis();
// 第二种方式,指定时间点开始执行
mUIhandler.postAtTime(new Runnable() {
@Override
public void run() {
Message msg = mUIhandler.obtainMessage();
msg.what = 1;
mUIhandler.sendMessage(msg);
}

}, tm + 15000);
       Handler提供了一个postAtTime()来处理业务,在使用时需要传递一个Runnable对象,在该对象的run()中执行相应的业务,同上,执行完后再发送消息给Handler的handleMessage()来处理,他的第二个参数是指定在某时间点来post到MessageQueue,这个参数需要根据SystemClock.uptimeMillis()方法来计算,这个方法返回的是系统开启启动的总时间,然后我们想要它在开机后的多长时间启动就加上那个时间就可以了。此外,Handler还提供了一个postDelayed()方法,此方法也是两个参数,第一个是Runnable对象,第二个是指定多少毫秒以后再执行,跟postAtTime()在使用上有点区别,但是本质上没有区别,因为postDelayed()的实现还是使用了SystemClock.uptimeMillis()+delayMillis来完成的。第二部分介绍原理时会说道。

3.使用Handler的Callback完成消息处理

final Handler callBackHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
String str = mContentTv.getText().toString();
mContentTv.setText(str + "\n\n第三种方式,使用Callback实现消息处理");
return false;
}
});
callBackHandler.post(new Runnable() {
@Override
public void run() {
Message msg = callBackHandler.obtainMessage();
msg.what = 1;
callBackHandler.sendMessage(msg);
}
});
      如上代码所示,我们在创建Handler对象时并没有直接重写其handleMessage()方法,而是在创建时传递了一个Callback对象,重写Callback的handleMessage()方法来完成消息处理。有一点不同的是Callback的handleMessage()方法有一个boolean返回值,而Handler的handleMessage()没有boolean返回值,所以前者更灵活一些,而且我们也可以使用Callback来灵活实现项目的“金字塔”分层的设计。因此,我们可以在不用继承Handler的基础上利用Handler.Callback来实现消息传递与处理,如果我们要实现一个Handler的子类,那就必须重写Handler的handleMessage()方法了。

      4.在子线程中创建Handler对象。

       记得我刚开始做Android开发入门在使用Handler时,总是牢记不能在主线程以外的地方创建使用Handler,然而,事实上我们可以在子线程中创建Handler,只需要在创建Handler的时候将主线程的Looper传递过去就行,如下所示:

new Thread(new Runnable() {
@Override
public void run() {
//获取主线程的Looper
Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper){
@Override
public void handleMessage(Message msg){
//这里消息业务
}
};

}
}).start();

    当然我们也可以不用这么麻烦去自己使用Looper来创建Handler对象,可以直接使用HandlerThread来帮我们完成。 关于HandlerThrad的用法详见http://blog.csdn.net/u012481172/article/details/44243681;
第23条。

    先调用Looper的getMainLooper()获取主线程的Looper,然后在创建Handler的时候再传递过去即可,这样Handler在sendMessage的时候就会将消息传递给主线程了。

    以上我们介绍了Handler的几种简单用法,但是如果是新手的话肯定会不明所以,糊里糊涂的,所以下面就会介绍Handler的原理。

第二部分 主线程和子线程的通信原理

      要使用Android的Handler进行主/子线程通信需要以下步骤:

      1、在线程中调用Looper.prepare()准备一个Looper。

      2、在线程中调用Looper.loop()进入消息循环队列。

      3、使用handler发送消息。

new Thread(new Runnable(){
@Override
public void run() {
//第一步,准备Looper
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
//处理消息业务
}
};
//第二步,进入消息循环
Looper.loop();
//发送消息
handler.sendEmptyMessage(1);

}

}).start();
      使用的步骤已经说明,现在说下其原理:

    首先调用Looper的prepare()方法准备一个Looper对象将其放入ThreadLocal中:

public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//new一个Looper对象,并放到ThreadLocal中
 sThreadLocal.set(new Looper(quitAllowed));
}
     可以看到,在创建一个Looper对象后就将该对象放到ThreadLocal中去,在调用myLooper()时获取该Looper对象,我们再看看Looper的构造器做了哪些事情呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}

      首先创建一个MessageQueue对象mQueue,在进行消息循环时(就是执行looper()方法)会不断从mQueue中读取消息。然后又调用Thread.currentThread()获取当前程序所在的线程。通常如果我们直接在UI线程中创建Handler时,这个mThread就是一个主线程,如果是在子线程中创建Handler并创建消息队列时,此mThread就是当前的子线程。以下是Looper.prepare()的工作流图:

   


      在Looper.prepare()完成后就可以调用loop()进入消息循环了,我们看下loop()的源码:

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//上面说到,直接调用myLooper()获取到在prepare()中创建的Looper对象,
//在myLooper()中只有一行代码:mThreadLocal.get()。
 final Looper me = myLooper();
//如果没有Looper对象就抛出异常:prepare()没有在这个线程中调用。
 if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取到mQueue对象,这个对象就是在Looper中创建的,也即在prepare()或prepareMainLooper()中创建
 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,这个地方有可能会出现阻塞情况,因为next()方法会有产生阻塞;如果为null就退出,
 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);
}
//Message的targer变量是一个Handler实例,当从MessageQueue中取出Message后就调用Message的一个
//Handler的dispatchMessage()方法
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();
}
}

     这里放一张我手绘的Looper.loop()的工作流图:

    


    上图中用红线连接起来的三个msg对象表示的是相同的实例。 

    looper()方法的逻辑很简单,基本都在注释上面说明了,这里再梳理一遍:首先获取到Looper对象,然后取出该Looper对象的MessageQueue对象,并进入无限循环,不断的调用MessageQueue的next()方法读取Message,如果没有就直接退出,如果有就取出Message的Handler并执行该Handler的dispatchMessage()方法。那么我们再去看一下Handler的dispatchMessage()方法是什么鬼:

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//判断Message的callback是否为空,如果不为空就执行handleCallback(msg),Message的callback变量就是一个Runnable
//如果该Runnable不为空就调用handleCallback(msg),里面就一行代码:msg.callback.start();
 if (msg.callback != null) {
handleCallback(msg);
} else {
//如果mCallback不为空就执行Callback的handleMessage。
 if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//执行Handler的handleMessage()
 handleMessage(msg);
}
}
      可以看到,在dispatchMessage()中最终调用了我们重写的handleMessage()方法。在dispatchMessage()方法中一系列的调用有一个优先级,即:首先如果Message的callback不为空就去执行,并且下面的Callback和Handler的handleMessage()都不会执行,如果Message的callback为空,就去判断Handler的Callback是否为空,不为空就去执行,如果执行成功就直接return掉,如果执行失败再去执行。总结一句就是:

       ① 如果Message的callback(即Runnable)部位null那么Callback和Handler的handleMessage()都不会执行;

       ② 如果Message的callback是null且Callback不为nul,就去执行Callback的handleMessage(),如果Callback的handleMessage()执行成功就不会执行Handler的handleMessage(), 如果执行失败就会执行Handler的handleMessage();

       ③ 如果Callback也是null,那么就直接去执行Handler的handleMessage()方法。

        所以显然,我们最常用的直接重写Handler的handleMessage()方法的优先级是最低的。

       那么上面所说的Message的callback是在什么时候被赋值的呢?换句话说我们在什么情况下会使用到呢?答案就是在获取Message对象的时候用到,请看下面的例子:

Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {

return false;
}
});
//获取一个Message对象
Message msg = Message.obtain(handler, new Runnable(){
@Override
public void run() {
//执行耗时操作
}

});
      如上所示使用Message的callback的场合,就是调用Message.obtain(handler,runnable)方法,该方法会将handler对象赋值给target变量,将runnable赋值给callback变量,target在Looper的loop()方法中会使用到,而callback会在Handler的dispatchMessage()中使用到。实际上当我们使用了上面的方式来获取一个Message时,最终的结果就是handler调用了它的dispatchMessage()然后导致了runnable的执行,这两个参数是有因果关系的,有了handler才会导致runnable执行。

      我们最常用的是使用Handler的obtainMessage()来获取一个Message对象的,查看其源码可以知道,实际上Handler的obtainMessage()也是调用了Message的obtainMessage(this)方法的,其中this表示当前的Handler。只不过用此种方式获取Message不会有callback创建。

      经过上面的分析我们知道了Looper是如何工作的,也知道了Handler的handlerMessage等方法是何时被调用的,也初步接触到MessageQueue,那么我们是如何将消息发送给MessageQueue的呢?接下来就将以下使用Handler发送消息的几种机制,我们从创建Handler说起。我们在使用Handler时一定会直接new一个Handler,它只提供一个public的构造器,因此只能通过new的方式创建,那么我们去看看他的构造器做了哪些事情:

    

/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
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());
}
}
//获取Looper,上面说过myLooper实际上就是把在prepare()中set到ThreadLocal中的Looper对象get出来。
mLooper = Looper.myLooper();
//如果mLooper为空,就说明我们没有调用prepare()
 if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//然后将Looper的mQueue赋值给Handler的mQueue,这个mQueue在发送消息时会用到。mLooper的mQueue也是在prepare()
//的时候创建的
 mQueue = mLooper.mQueue;
mCallback = null;
}
      创建完Handler后我们就需要获取Message对象了,关于获取Message对象,上面已经说过了,就不在赘述。接下来就是发送消息了。

      使用Handler发送消息(Message)可以使用两个系列的方法实现,第一就是post(...)系列,第二就是sendMessage(...)系列。

      1.post(...)系列:

     (1)post(Runnable):似乎看起来没有使用到Message,但其实不然,因为在post(Runnable)方法中系统会帮我们创建一个Message对象,并且把Runnable对象赋值给该Message的callback,所以,如果使用了post(Runnable)方法,那么Handler的handleMessage()和Callback的handleMessage()都将无效。

     (2)postAtTime(Runnable,miultTime):指定某时间点再去执行,原理同post(Runnable)一样。

              ....

      2.sendMessage(...)系列:

     (1)sendMessage(msg):就是单纯的发送一个Message。

              ....

              ....

       不管是什么方法发送消息,最后都会调用到 sendMessageAtTime(Message msg, long uptimeMillis)方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
       第一个参数就是一个Message对象,第二个参数就是延迟执行的时间,如果我们在使用时没有使用到这个参数就表示立刻去执行。可以看到,在该方法中,首先将当前的Handler对象赋值给Message的target变量,随后调用mQueue的enqueueMessage(msg,uptimeMillis)方法。需要注意这个mQueue变量是我们在创建Handler对象的时候从Looper中获取的,从根本上来讲的就是Looper调用prepare()的时候创建的。然后我们再去看看MessageQueue的enqueueMessage()方法做了哪些事情:

final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}

boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}

msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue.  Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
        初步的调用流程就介绍到这里,在这里并没有更深层次的分析native层的使用,仅仅很肤浅的介绍了下其工作原理,最后总结一下其调用流程:

      首先Looper调用prepare()方法准备好一个MessageQueue,当然,一个app开启后主线程自己就创建了一个MessageQueue,在ActivityThread线程(就是主线程)中,Framework已经调用了Looper的prepare()并进入了loop()。当prepare()准备好了之后就调用loop()方法进入消息循环,当然第一次是没有消息的,因此会处于阻塞状态(在MessageQueue的next()方法处),如果有消息就读取一个Message,然后调用Message的dispatchMessage(),然后根据优先级以此调用Message的callback(一个Runnable对象)、Handler.Callback的handleMessage()、Handler的handleMessage()。另一方面,当我们要发送一个消息时,通常会调用sendMessage()或postMessage()等方法,本质上都是调用了sendMessageAtTime()方法,消息发送成功后会唤醒looper()继续执行。然后就调用到了dispatchMessage()了。

         最后,关于Looper我们通常不会用到,除非你需要自己维护一个消息队列,而不使用主线程的消息队列,关于Message和Handler还有很多有用的方法可以使用,具体的自己一看便知了。最后放几张手绘的流程图!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android Handler Message