码迷,mamicode.com
首页 > 其他好文 > 详细

理解Thread,Looper,MessageQueue,Handler关系

时间:2016-04-22 20:39:59      阅读:319      评论:0      收藏:0      [点我收藏+]

标签:

概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制。我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能。但作为程序员,我不能只知道怎么用Handler,还要知道其内部如何实现的。Handler的内部实现主要涉及到如下几个类: Thread、MessageQueue和Looper。这几类之间的关系可以用如下的图来简单说明:

技术分享

Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上,我们通过Handler间接地与下面这几个相对底层一点的类打交道。

一图胜千言

我们在本文讨论了Thread、MessageQueue、Looper以及Hanlder的之间的关系,我们可以通过如下一张传送带的图来更形象的理解他们之间的关系。

技术分享

在现实生活的生产生活中,存在着各种各样的传送带,传送带上面洒满了各种货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另一端进行收集处理。

我们可以把传送带上的货物看做是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,我们可以把发送机滚轮看做是Looper,而发动机的转动是需要电源的,我们可以把电源看做是线程Thread,所有的消息循环的一切操作都是基于某个线程的。一切准备就绪,我们只需要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当我们按下开关的时候,我们就相当于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。

那Hanlder在传送带模型中相当于什么呢?我们可以将Handler看做是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另一端顺着管道划出传送带。我们在传送带的一端放入货物的操作就相当于我们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另一端顺着管道划出时,我们就相当于调用了Hanlder的dispatchMessage方法,在该方法中我们完成对Message的处理。

下面重点介绍Handler:

Handler是暴露给开发者最顶层的一个类,其构建在Thread、Looper与MessageQueue之上。 
Handler具有多个构造函数,签名分别如下所示: 

  • 1. publicHandler()
  • 2. publicHandler(Callbackcallback)
  • 3. publicHandler(Looperlooper)
  • 4. publicHandler(Looperlooper, Callbackcallback) 

第1个和第2个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。 
第3个和第4个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。 
第2个和第4个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:

?
1
2
3
public interface Callback {
  public boolean handleMessage(Message msg);
}

Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法: 
1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法 
2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法 
也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。 
在Java中,如果我们想使用多线程,有两种办法: 
1. 向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法 
2. 无需向Thread的构造函数传入Runnable对象,但是要重写Thread本身的run方法 
所以只要用过多线程Thread,应该就对Hanlder这种需要实现handleMessage的两种方式了然于心了。

我们知道通过sendMessageXXX系列方法可以向消息队列中添加消息,我们通过源码可以看出这些方法的调用顺序, 
sendMessage调用了sendMessageDelayed,sendMessageDelayed又调用了sendMessageAtTime。 
Handler中还有一系列的sendEmptyMessageXXX方法,而这些sendEmptyMessageXXX方法在其内部又分别调用了其对应的sendMessageXXX方法。

通过以下调用关系图我们可以看的更清楚些: 

技术分享


由此可见所有的sendMessageXXX方法和sendEmptyMessageXXX最终都调用了sendMessageAtTime方法。

我们再来看看postXXX方法,会发现postXXX方法在其内部又调用了对应的sendMessageXXX方法,我们可以查看下sendMessage的源码:

?
1
2
3
4
public final boolean post(Runnable r)
{
  return sendMessageDelayed(getPostMessage(r), 0);
}

可以看到内部调用了getPostMessage方法,该方法传入一个Runnable对象,得到一个Message对象,getPostMessage的源码如下:

?
1
2
3
4
5
private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();
  m.callback = r;
  return m;
 }

通过上面的代码我们可以看到在getPostMessage方法中,我们创建了一个Message对象,并将传入的Runnable对象赋值给Message的callback成员字段,然后返回该Message,然后在post方法中该携带有Runnable信息的Message传入到sendMessageDelayed方法中。由此我们可以看到所有的postXXX方法内部都需要借助sendMessageXXX方法来实现,所以postXXX与sendMessageXXX并不是对立关系,而是postXXX依赖sendMessageXXX,所以postXXX方法可以通过sendMessageXXX方法向消息队列中传入消息,只不过通过postXXX方法向消息队列中传入的消息都携带有Runnable对象(Message.callback)。

我们可以通过如下关系图看清楚postXXX系列方法与sendMessageXXX方法之间的调用关系: 

技术分享


通过分别分析sendEmptyMessageXXX、postXXX方法与sendMessageXXX方法之间的关系,我们可以看到在Handler中所有可以直接或间接向消息队列发送Message的方法最终都调用了sendMessageAtTime方法,该方法的源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  MessageQueue queue = mQueue;
  if (queue == null) {
   RuntimeException e = new RuntimeException(
     this + " sendMessageAtTime() called with no mQueue");
   Log.w("Looper", e.getMessage(), e);
   return false;
  }
  //注意下面这行代码
  return enqueueMessage(queue, msg, uptimeMillis);
}

该方法内部调用了enqueueMessage方法,该方法的源码如下:

?
1
2
3
4
5
6
7
8
9
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  //注意下面这行代码
  msg.target = this;
  if (mAsynchronous) {
   msg.setAsynchronous(true);
  }
  //注意下面这行代码
  return queue.enqueueMessage(msg, uptimeMillis);
}

在该方法中有两件事需要注意: 

  • 1. msg.target = this

该代码将Message的target绑定为当前的Handler

  • 2. queue.enqueueMessage

变量queue表示的是Handler所绑定的消息队列MessageQueue,通过调用queue.enqueueMessage(msg, uptimeMillis)我们将Message放入到消息队列中。

所以我们通过下图可以看到完整的方法调用顺序: 

技术分享

我们在分析Looper.loop()的源码时发现,Looper一直在不断的从消息队列中通过MessageQueue的next方法获取Message,然后通过代码msg.target.dispatchMessage(msg)让该msg所绑定的Handler(Message.target)执行dispatchMessage方法以实现对Message的处理。 
Handler的dispatchMessage的源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(Message msg) {
  //注意下面这行代码
  if (msg.callback != null) {
   handleCallback(msg);
  } else {
    //注意下面这行代码
   if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
     return;
    }
   }
    //注意下面这行代码
   handleMessage(msg);
  }
}

我们来分析下这段代码:

1.首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:

?
1
2
3
private static void handleCallback(Message message) {
  message.callback.run();
}

这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。

2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。

3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。

综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。


===============================================================================================================

备注:

4.线程间信息共享  --  Hander 消息传递机制
各类之间的关系
在Java层,创建了一个Looper对象,这个Looper对象是用来进入消息循环的,它的内部有一个消息队列MessageQueue来与底层交互,并且在Handler内部拥有两者的引用。
在JNI层,创建了一个NativeMessageQueue对象,类似于一个桥梁保存在MessageQueue中,用来实现Java层与底层之间的交互
在C++层,创建了一个Looper对象,该对象的作用是:
当Java层的消息队列中没有消息时,就使Looper所在线程进行睡眠
当Java层的消息队列中来了新的消息后,就唤醒Looper所在线程

5.Handler是什么

那么Handler到底是什么呢?Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制。每个Hanlder都关联了一个线程,每个线程内部都维护了一个消息队列MessageQueue,这样Handler实际上也就关联了一个消息队列。可以通过Handler将Message和Runnable对象发送到该Handler所关联线程的MessageQueue(消息队列)中,然后该消息队列一直在循环拿出一个Message,对其进行处理,处理完之后拿出下一个Message,继续进行处理,周而复始。当创建一个Handler的时候,该Handler就绑定了当前创建Hanlder的线程。从这时起,该Hanlder就可以发送Message和Runnable对象到该Handler对应的消息队列中,当从MessageQueue取出某个Message时,会让Handler对其进行处理。

6.Loop类 介绍
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.
Most interaction with a message loop is through the Handler class.

7.Handler 发送消息办法 效果一致
 mHandler.obtainMessage(MSG_AIRPLANE,isAirplaneOpen).sendToTarget() ;  
            
            Message message = new Message();
            message.what = MSG_AIRPLANE;
            message.obj = isAirplaneOpen;
            mHandler.sendMessage(message);

8.Looper 类用来为一个线程跑一个消息循环。
  线程在默认情况下是没有消息循环与之关联的,Thread类在run()方法中的内容执行完之后就退出了,即线程做完自己的工作之后就结束了,没有循环的概念。
  调用Looper类的 prepare() 方法可以为当前线程创建一个消息循环,调用loop() 方法使之处理信息,直到循环结束。

9.一个线程对应一个Looper,有一个消息队列,但是可以关联多个Handlers

10.消息循环
  消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。
  如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;
  如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。

11.线程是程序运行的单位,是动力,也是构建其他类的基础,所以是主线程,还是非UI线程,他们创建之后,再构建Looper类, 通过Looper类再构建MessageQueue类,最后构建
(1)线程Thread类 通过 Loop.prepare()方法构建 一个Looper消息循环对象
public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException(
                    "Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }


(2)消息循环Looper类通过 构造函数Looper() 初始化一个MessageQueue类
 private Looper() {
        // 私有构造方法,在prepare()方法里面调用
        // 创建消息队列
        mQueue = new MessageQueue();
        mRun = true;
        // 当前线程
        mThread = Thread.currentThread();
    }
(3)Handler 是如何关联 Thread类里面的Looper 和 MessageQueue类的?
Handler类.
Handler类源码中申明了 
 	final MessageQueue mQueue;
        final Looper mLooper;   
表示Handler关联了线程中的looper对象 和 queue对象.  关联的方式是通过 Handler的构造方法 eg:
1. public Handler()
2. public Handler(Callbackcallback)
3. public Handler(Looperlooper)
4. public Handler(Looperlooper, Callbackcallback) 
第1个和第2个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。 
第3个和第4个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。 
第2个和第4个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,
Handler是暴露给开发者最顶层的一个类,其构建在Thread、Looper与MessageQueue之上。 

12.Handler构造:
  Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。
  前面说过,不是所有线程都有一个消息循环,所以如果当前线程没有消息循环,而构造Handler对象时又没有指定Looper对象,则会抛出一个运行时异常:
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
  如果没有抛出异常,Handler对象构造好之后,它就关联了相应的Looper实例和消息队列实例,即完成绑定。


       参考:http://www.cnblogs.com/mengdd/p/3601294.html

理解Thread,Looper,MessageQueue,Handler关系

标签:

原文地址:http://blog.csdn.net/zqs62761130/article/details/51200874

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!