一说到Android的消息机制,自然就会联想到Handler,我们知道Handler是Android消息机制的上层接口,因此我们在开发过程中也只需要和Handler交互即可,很多人认为Handler的作用就是更新UI,这也确实没错,但除了更新UI,Handler其实还有很多其他用途,比如我们需要在子线程进行耗时的I/O操作,可能是读取某些文件或者去访问网络等,当耗时操作完成后我们可能需要在UI上做出相应的改变,但由于Android系统的限制,我们是不能在子线程更新UI控件的,否则就会报异常,这个时候Handler就可以派上用场了,我们可以通过Handler切换到主线程中执行UI更新操作。

1.Handler和Looper

当Handler实例化的时候,Looper会自动关联Handler所在线程,如果Looper什么都不设置,默认关联的就是主线程

2.Looper和MessageQueue
 当Looper实例化的时候,内部会初始化MessageQueue消息队列,消息队列如果和主线程关联,是打开的;如果不是主线程,要自己操作(消息队列打开就能接受消息,不打开就不接受消息)。
有专门控制的标志变量

3.Handler和Message
 可以对Message操作,发送,删除

4.Handler和MessageQueue
 Handler发送的消息由MessageQueue统一处理,MessageQueue通过回调方法可以确认该消息已经处理过了
 是通过回调接口用的回调方法

5.Message和MessageQueue
MessageQueue里面存放Message

下面是Handler一些常用方法:

void  handleMessage(Message msg):处理消息的方法,该方法通常会被重写。

final boolean hasMessages(int what):检测消息队列中是否包含what属性为指定值的消息。

Message obtainMessage():获取消息的方法,此函数有多个重载方法。

sendEmptyMessage(int what):发送空消息。

final boolean sendEmptyMessageDelayed(int what , long delayMillis):指定多少毫秒后发送空消息。

final boolean sendMessage(Message msg):立即发送消息。

final boolean sendMessageDelayed(Message msg ,long delayMillis):指定多少毫秒后发送消息。

final boolean post(Runnable r):执行runnable操作。

final boolean postAtTime(Runnable r, long upTimeMillis):在指定时间执行runnable操作。

final boolean postDelayed(Runnable r, long delayMillis):指定多少毫秒后执行runnable操作。

介绍完方法后,我们就从一个简单的例子入手吧,然后一步步的分析:

[java] view plain copy
  1. public class MainActivity extends AppCompatActivity {
  2. public static final int MSG_FINISH = 0X001;
  3. //创建一个Handler的匿名内部类
  4. private Handler handler = new Handler() {
  5. @Override
  6. public void handleMessage(Message msg) {
  7. switch (msg.what) {
  8. case MSG_FINISH:
  9. LogUtils.e("handler所在的线程id是-->" + Thread.currentThread().getName());
  10. break;
  11. }
  12. }
  13. };
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. //启动耗时操作
  19. consumeTimeThread(findViewById(R.id.tv));
  20. //        handler.post()
  21. }
  22. //启动一个耗时线程
  23. public void consumeTimeThread(View view) {
  24. new Thread() {
  25. public void run() {
  26. try {
  27. LogUtils.e("耗时子线程的Name是--->" + Thread.currentThread().getName());
  28. //在子线程运行
  29. Thread.sleep(2000);
  30. //完成后,发送下载完成消息
  31. handler.sendEmptyMessage(MSG_FINISH);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }.start();
  37. }
  38. }

运行结果:

上面的例子其实就是Handler的基本使用,在主线中创建了一个Handler对象,然后通过在子线程中模拟一个耗时操作完成后通过sendEmptyMessage(int)方法发送一个消息通知主线程的Handler去执行相应的操作。通过运行结果我们也可以知道Handler确实也是在主线程运行的。

那么问题来了,通过Handler发送的消息是怎么到达主线程的呢?接下来我们就来掰掰其中的奥妙,前方高能,请集中注意力!为了更好的理解Handler的工作原理,我们先来介绍与Handler一起工作的几个组件:

Message:Handler接收和处理消息的对象。

Looper:每个线程只能有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息后把消息发送给Handler进行处理。

MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时,会在它的构造方法中创建MessageQueue对象。

Handler:它的作用有两个—发送消息和处理消息,程序使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则消息就没有在MessageQueue进行保存了。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。我们先对上面的几个组件有大概的了解就好,后面我们都会详细分析,既然消息是从Handler发送出去,那么我们就先从Handler入手吧。先来看看Handler 的构造方法源码:

[java] view plain copy
  1. public class Handler {
  2. /**
  3. * 未实现的空方法handleMessage()
  4. */
  5. public void handleMessage(Message msg) {
  6. }
  7. /**
  8. * 我们通常用于创建Handler的构造方法之一
  9. */
  10. public Handler() {
  11. this(null, false);
  12. }
  13. // 构造方法的内调用的this(null, false)的具体实现
  14. public Handler(Callback callback, boolean async) {
  15. //检查Handler是否是static的,如果不是的,那么有可能导致内存泄露
  16. if (FIND_POTENTIAL_LEAKS) {
  17. final Class<? extends Handler> klass = getClass();
  18. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  19. (klass.getModifiers() & Modifier.STATIC) == 0) {
  20. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  21. klass.getCanonicalName());
  22. }
  23. }
  24. //重要的组件出现啦!Looper我们先理解成一个消息队列的管理者,用来从消息队列中取消息的,后续会详细分析
  25. mLooper = Looper.myLooper();
  26. if (mLooper == null) {
  27. //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler
  28. //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法
  29. throw new RuntimeException(
  30. "Can't create handler inside thread that has not called Looper.prepare()");
  31. }
  32. //将mLooper里面的消息队列复制到自身的mQueue,这也就意味着Handler和Looper是公用一个消息队列
  33. mQueue = mLooper.mQueue;
  34. //回调函数默认是Null
  35. mCallback = null;
  36. }

分析:Handler的构造方法源码不是很多,也比较简单,但是我们从源码中也可以得知,在创建Handler时,Handler内部会去创建一个Looper对象,这个Looper对象是通过Looper.myLooper()创建的(后续会分析这个方法),同时还会创建一个消息队列MessageQueue,而这个MessageQueue是从Looper中获取的,这也就意味着Handler和Looper共用一个消息队列,当然此时Handler,Looper以及MessageQueue已经捆绑到一起了。上面还有一个情况要说明的,那就是:

[java] view plain copy
  1. if (mLooper == null) {
  2. //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler
  3. //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法
  4. throw new RuntimeException(
  5. "Can't create handler inside thread that has not called Looper.prepare()");
  6. }  </span>

这里先回去判断Looper是否为空,如果为null,那么就会报错,这个错误对我们来说应该比较熟悉吧,那为什么会报这个错误呢?我们在前面说过Handler的作用有两个—发送消息和处理消息,我们在使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则就无法进行消息循环。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。那么又该如何保障当前线程中一定有Looper对象呢?这里其实分两种情况:

(1)在主UI线程中,系统已经初始化好了一个Looper对象,因此我们可以直接创建Handler并使用即可。

(2)在子线程中,我们就必须自己手动去创建一个Looper对象,并且去启动它,才可以使用Handler进行消息发送与处理。使用事例如下:

[java] view plain copy
  1. class childThread extends Thread{
  2. public Handler mHandler;
  3. @Override
  4. public void run() {
  5. //子线程中必须先创建Looper
  6. Looper.prepare();
  7. mHandler =new Handler(){
  8. @Override
  9. public void handleMessage(Message msg) {
  10. super.handleMessage(msg);
  11. //处理消息
  12. }
  13. };
  14. //启动looper循环
  15. Looper.loop();
  16. }
  17. }

分析完Handler的构造方法,我们接着看看通过Handler发送的消息到底是发送到哪里了?我们先来看看Handler的几个主要方法源码:

[java] view plain copy
  1. // 发送一个空消息的方法,实际上添加到MessagerQueue队列中
  2. public final boolean sendEmptyMessage(int what) {
  3. return sendEmptyMessageDelayed(what, 0);
  4. }
  5. // 给上一个方法调用
  6. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  7. Message msg = Message.obtain();
  8. msg.what = what;
  9. return sendMessageDelayed(msg, delayMillis);
  10. }
  11. // 给上一个方法调用
  12. public final boolean sendMessageDelayed(Message msg, long delayMillis) {
  13. if (delayMillis < 0) {
  14. delayMillis = 0;
  15. }
  16. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  17. }
  18. // 给上一个方法调用
  19. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  20. MessageQueue queue = mQueue;
  21. if (queue == null) {
  22. RuntimeException e = new RuntimeException(this
  23. + " sendMessageAtTime() called with no mQueue");
  24. Log.w("Looper", e.getMessage(), e);
  25. return false;
  26. }
  27. return enqueueMessage(queue, msg, uptimeMillis);
  28. }
  29. // 最后调用此方法添加到消息队列中
  30. private boolean enqueueMessage(MessageQueue queue, Message msg,
  31. long uptimeMillis) {
  32. msg.target = this;// 设置发送目标对象是Handler本身
  33. if (mAsynchronous) {
  34. msg.setAsynchronous(true);
  35. }
  36. return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息队列中
  37. }
  38. // 在looper类中的loop()方法内部调用的方法
  39. public void dispatchMessage(Message msg) {
  40. if (msg.callback != null) {
  41. handleCallback(msg);
  42. } else {
  43. if (mCallback != null) {
  44. if (mCallback.handleMessage(msg)) {
  45. return;
  46. }
  47. }
  48. handleMessage(msg);
  49. }
  50. }

分析:通过源码我们可以知道,当我们调用sendEmptyMessage(int)发送消息后。最终Handler内部会去调用enqueueMessage(MessageQueue queue,Message msg)方法把发送的消息添加到消息队列MessageQueue中,同时还有设置msg.target=this此时就把当前handler对象绑定到msg.target中了,这样就完成了Handler向消息队列存放消息的过程。这个还有一个要注意的方法 dispatchMessage(Message),这个方法最终会在looper中被调用(这里我们先知道这点就行,后续还会分析)。话说我们一直在说MessageQueue消息队列,但这个消息队列到底是什么啊?其实在Android中的消息队列指的也是MessageQueue,MessageQueue主要包含了两种操作,插入和读取,而读取操作本身也会伴随着删除操作,插入和读取对应的分别是enqueueMessage和next,其中enqueueMessage是向消息队列中插入一条消息,而next的作用则是从消息队列中取出一条消息并将其从队列中删除。虽然我们一直称其为消息队列但是它的内部实现并不是队列,而是通过一个单链表的数据结构来维护消息列表的,因为我们知道单链表在插入和删除上比较有优势。至内MessageQueue的内部实现,这个属于数据结构的范畴,我们就不过多讨论了,还是回到原来的主题上来,到这里我们都知道Handler发送的消息最终会添加到MessageQueue中,但到达MessageQueue后消息又是如何处理的呢?还记得我们前面说过MessageQueue是由Looper负责管理的吧,现在我们就来看看Looper到底是如何管理MessageQueue的?

[java] view plain copy
  1. public final class Looper {
  2. // sThreadLocal.get() will return null unless you've called prepare().
  3. //存放线程的容器类,为确保获取的线程和原来的一样
  4. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  5. private static Looper sMainLooper; // guarded by Looper.class
  6. //消息队列
  7. final MessageQueue mQueue;
  8. final Thread mThread;
  9. //perpare()方法,用来初始化一个Looper对象
  10. public static void prepare() {
  11. prepare(true);
  12. }
  13. private static void prepare(boolean quitAllowed) {
  14. if (sThreadLocal.get() != null) {
  15. throw new RuntimeException("Only one Looper may be created per thread");
  16. }
  17. sThreadLocal.set(new Looper(quitAllowed));
  18. }
  19. //handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。
  20. public static Looper myLooper() {
  21. return sThreadLocal.get();
  22. }
  23. //Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象
  24. private Looper(boolean quitAllowed) {
  25. mQueue = new MessageQueue(quitAllowed);
  26. mRun = true;
  27. mThread = Thread.currentThread();
  28. }
  29. //这个方法是给系统调用的,UI线程通过调用这个线程,从而保证UI线程里有一个Looper
  30. //需要注意:如果一个线程是UI线程,那么myLooper和getMainLooper是同一个Looper
  31. public static final void prepareMainLooper() {
  32. prepare();
  33. setMainLooper(myLooper());
  34. if (Process.supportsProcesses()) {
  35. myLooper().mQueue.mQuitAllowed = false;
  36. }
  37. }
  38. //获得UI线程的Looper,通常我们想Hanlder的handleMessage在UI线程执行时通常会new  Handler(getMainLooper());
  39. public synchronized static final Looper getMainLooper() {
  40. return mMainLooper;
  41. }
  42. //looper中最重要的方法loop(),该方法是个死循环,会不断去消息队列MessageQueue中获取消息,然后调dispatchMessage(msg)方法去执行
  43. public static void loop() {
  44. final Looper me = myLooper();
  45. if (me == null) {
  46. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  47. }
  48. final MessageQueue queue = me.mQueue;
  49. //死循环
  50. for (;;) {
  51. Message msg = queue.next(); // might block
  52. if (msg == null) {
  53. // No message indicates that the message queue is quitting.
  54. return;
  55. }
  56. //这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法
  57. msg.target.dispatchMessage(msg);
  58. msg.recycle();
  59. }
  60. }

分析:代码不算多,我们拆分开慢慢说,在Looper源码中我们可以得知其内部是通过一个ThreadLocal的容器来存放Looper的对象本身的,这样就可以确保每个线程获取到的looper都是唯一的。那么Looper对象是如何被创建的呢?通过源码我们可以知道perpare()方法就可以创建Looper对象:

[java] view plain copy
  1. //perpare()方法,用来初始化一个Looper对象
  2. public static void prepare() {
  3. prepare(true);
  4. }
  5. private static void prepare(boolean quitAllowed) {
  6. if (sThreadLocal.get() != null) {
  7. throw new RuntimeException("Only one Looper may be created per thread");
  8. }
  9. sThreadLocal.set(new Looper(quitAllowed));
  10. }

在创建Looper对象前先会去判断ThreadLocal中是否已经存在Looper对象,如果不存在就新创建一个Looper对象并且存放ThreadLocal中。这里还有一个要注意的是在Looper创建的同时MessageQueue消息队列也被创建完成,这样的话Looper中就持有了MessageQueue对象。

[java] view plain copy
  1. //Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象
  2. private Looper(boolean quitAllowed) {
  3. mQueue = new MessageQueue(quitAllowed);
  4. mRun = true;
  5. mThread = Thread.currentThread();
  6. }

那么我们如何获取已经创建好的Looper对象呢?通过源码我们知道myLooper()方法就可以获取到Looper对象:

[java] view plain copy
  1. //handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。
  2. public static Looper myLooper() {
  3. return sThreadLocal.get();
  4. }

Looper对象的创建和获取,还有MessageQueue对象的创建,现在我们都很清楚了,但是Looper到底是怎么管理MessageQueue对象的呢?这就要看looper()方法了:

[java] view plain copy
  1. //looper中最重要的方法loop(),该方法是个死循环,
  2. //会不断去消息队列MessageQueue中获取消息,
  3. //然后调dispatchMessage(msg)方法去执行
  4. public static void loop() {
  5. final Looper me = myLooper();
  6. if (me == null) {
  7. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  8. }
  9. final MessageQueue queue = me.mQueue;
  10. //死循环
  11. for (;;) {
  12. Message msg = queue.next(); // might block
  13. if (msg == null) {
  14. // No message indicates that the message queue is quitting.
  15. return;
  16. }
  17. //这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法
  18. msg.target.dispatchMessage(msg);
  19. msg.recycle();
  20. }

通过looper()方法内部源码我们可以知道,首先会通过myLoooper()去获取一个Looper对象,如果Looper对象为null,就会报出一个我们非常熟悉的错误提示,“No Looper;Looper.prepare() wasn't called on this thread”,要求我们先通过Looper.prepare()方法去创建Looper对象;如果Looper不为null,那么就会去获取消息队列MessageQueue对象,接着就进入一个for的死循环,不断从消息队列MessageQueue对象中获取消息,如果消息不为空,那么久会调用msg.target的dispatchMessage(Message)方法,那么这个target又是什么,没错target就是我们创建的Handler对象,还记得我们前面分析Handler源码时说过的那个方法嘛?

[java] view plain copy
  1. // 在looper类中的loop()方法内部调用的方法
  2. public void dispatchMessage(Message msg) {
  3. if (msg.callback != null) {
  4. handleCallback(msg);
  5. } else {
  6. if (mCallback != null) {
  7. if (mCallback.handleMessage(msg)) {
  8. return;
  9. }
  10. }
  11. handleMessage(msg);
  12. }

现在明白了吧?首先,检测Message的callback是否为null,不为null就通过handleCallback方法来处理消息,那么Message的callback是什么?其实就是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数,我们顺便看看post方法源码:

[java] view plain copy
  1. public final boolean post(Runnable r)
  2. {
  3. return  sendMessageDelayed(getPostMessage(r), 0);
  4. }

getPostMessage(Runnable r)方法源码:

[java] view plain copy
  1. private static Message getPostMessage(Runnable r) {
  2. Message m = Message.obtain();
  3. m.callback = r;
  4. return m;
  5. }

现在明白Message的callback是什么了吧?而对应handleCallback方法逻辑也比较简单:

[java] view plain copy
  1. private static void handleCallback(Message message) {
  2. message.callback.run();
  3. }

嗯,是的,因此最终执行的还是通过post方法传递进来的Runnable参数的run方法。好了,我们继续dispatchMessage()方法的分析,接着会去检查mCallback是否为null,不为null,则调用mCallback的handleMessage方法来处理消息。至于Callback则就是一个接口定义如下:

[java] view plain copy
  1. /**
  2. * Callback interface you can use when instantiating a Handler to avoid
  3. * having to implement your own subclass of Handler.
  4. *
  5. * @param msg A {@link android.os.Message Message} object
  6. * @return True if no further handling is desired
  7. */
  8. public interface Callback {
  9. public boolean handleMessage(Message msg);
  10. }

这个接口有什么用呢?其实通过Callback接口我们就可以采取如下方法来创建Handler对象:

[java] view plain copy
  1. Handler handler =new Handler(callback)

那么这样做到底有什么意义,其实这样做可以用callback来创建一个Handler的实例而无需派生Handler的子类。在我们的开发过程中,我们经常使用的方法就是派生一个Hanlder子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种方式,那就是当我们不想派生子类的时候,可以通过Callback来实现。 继续dispatchMessage()方法的分析,最后如果以上条件都不成立的话,就会去调用Handler的handleMessage方法来处理消息。而 我们的Handler是在主线程创建的,也就是说Looper也是主线程的Looper,因此handleMessage内部处理最终都会在主线程上执行,就这样整个流程都执行完了。下面提供一个图解帮助大家理解:

最后我们来个小总结:Android中的Looper类主要作用是来封装消息循环和消息队列的,用于在android线程中进行消息处理。handler是用来向消息队列中插入消息的并最好对消息进行处理。

 (1) Looper类主要是为每个线程开启的单独的消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环) Looper对象负责管理MessageQueue,而MessageQueue主要是用来存放handler发送的消息,而且一个线程只能有一个Looper,对应一个MessageQueue。 
(2) 我们通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper中的MessageQueue发送消息并且Handler还必须定义自己的处理方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,如Handler在主线程中定义,它是与主线程的Looper绑定。 
mainHandler = new Handler() 等价于 new Handler(Looper.myLooper())
Looper.myLooper():获取当前进程的looper对象, Looper.getMainLooper() 用于获取主线程的Looper对象。 
(3) 在非主线程中直接new Handler() 会报如下的错误:  Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper,然后再调用Looper.loop()。
(4) Looper.loop():启动looper中的循环线程,Handler就会从消息队列里取消息并进行对应处理。 
 最后要注意的是写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop()才会中止,其后的代码才能得以运行。

看了不少关于Handler、Looper、MessageQueue之间关系的文章。感觉挺枯燥的,上来就是一团代码,看着心烦。

总结: 后来我捋了捋,画了个图。先看图,我们再来谈他们间的关系:

在这个图中,我做了个类比:(很重要,多看几遍)

MessageQueue,流水线上的"履带";

Looper,履带的"驱动轮";

Handler,流水线上的"工人";

Message,流水线上的"包裹"。

现在让我们来解释这三者间的工作关系,首先,我们要明确一个基本法:

一个Thread只能有且只能一个Looper。一个Looper只能对应一个Message。一个Looper和MessageQueue的绑定体可以对应多个Handler。(参考上图)

1.Looper与MessageQueue

刚刚说了一个Thread只能有一个Looper。为什么只能有一个呢?让我们去看它的一个方法Looper.prepare()方法的源码:

[java] view plain copy
  1. public static final void prepare() {
  2. if (sThreadLocal.get() != null) {
  3. throw new RuntimeException("Only one Looper may be created per thread");
  4. }
  5. sThreadLocal.set(new Looper(true));
  6. }

第二行,如果当前线程已有一个Looper,那么将直接抛出异常。这是基本法,没得说。

然后我们再来说说一个线程一般要怎么创建一个Looper,例子A:(下面会拿这个例子讲解)

[java] view plain copy
  1. class LooperThread extends Thread  {
  2. public Handler mHandler;
  3. public void run()   {
  4. Looper.prepare();
  5. mHandler = new Handler()   {
  6. public void handleMessage(Message msg) {
  7. // 你的方法
  8. }
  9. };
  10. }
  11. Looper.loop();
  12. }

首先,一个线程先Looper.prepare(),创建一个Looper,在这个prepare()方法中,回头看一眼基本法里的代码,它会在 return 中new 一个(Looper(true));其实,就是下面的代码:

[java] view plain copy
  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mRun = true;
  4. mThread = Thread.currentThread();  //绑定当前线程
  5. }

注意第二行,它创建并绑定了一个MessageQueue,做个类比,就是给Looper这个驱动轮套上了它的履带MessageQueue,由于Looper在当前线程唯一,则其应为一一对应关系。

现在驱动轮有了,履带有了,要让这个流水线动起来,显然,还需要另外一个操作。

没错,这个操作就是例子A中倒二行的Looper.loop()。其源码为:(仅列出你需要理解的代码,其他你不需要关心)

[java] view plain copy
  1. public static void loop() {
  2. final Looper me = myLooper();//获取当前线程的Looper
  3. if (me == null) {  //如果没有为当前线程进行Looper.prepare(),跳出异常
  4. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  5. }
  6. final MessageQueue queue = me.mQueue; //获得当前Looper me所绑定的MessageQueue
  7. for (;;) {  //无限循环
  8. Message msg = queue.next(); // 通过MessageQueue的next()方法从队首取一个消息
  9. if (msg == null) {
  10. //如果消息队列为空,就退出这个无限循环
  11. return;
  12. }
  13. msg.target.dispatchMessage(msg);  //分发消息,msg.target其实就是个handler,你先记着不要管
  14. msg.recycle();  //通过MessageQueue的recycle()方法回收资源,让"履带"转动取来
  15. }
  16. }

Tips:这里需要注意一个事情,观察到loop()方法里有个无限循环。在例子A中,如果你在Looper.loop()后面写代码,IDE会判断这些代码是不会执行的,因此会报错。也就说,loop()方法必须在该线程需要执行的内容的最后一行写!!!

上面的loop()中我给出了详细的注解,你稍微花两分钟就能看明白。第8行,从MessageQueue队列中取走第一个消息,然后在第13行,调用msg.target的disspachMessage()方法,分发这个消息。其实msg.target就是个handler,至于为什么,我会在Handler的部分进行讲解,你先这个记着。

Looper部分总结一句话:

在线程的开头,为这个线程准备一个Looper(Looper.prepare()),这个Looper会自动绑定上一个MessageQueue,然后在线程的最后,通过Looper.loop()方法,让这个MessageQueue转动起来,传送这个MessageQueue上的消息对象Message。每次Message被传送到队首,这个Message会被disspatchMessage()方法分发出去,分配到管理它的Handler(流水线工人)那里进行处理。

2.Message

我们在图中做了一个类比,把Message当成了一个包裹,那么它里面到底包裹了啥?我们来看一下它的源码:

[java] view plain copy
  1. public final class Message implements Parcelable {
  2. public int what;
  3. public int arg1;
  4. public int arg2;
  5. public Object obj;//以上是一些数据类型,无视
  6. public Messenger replyTo;
  7. Bundle data;//一般用于封装数据的包
  8. Handler target;//注意看这里,是个Handler
  9. Runnable callback;//注意这个回调
  10. ......
  11. }

看到那个target了么,就是个Handler。现在回去看我的那个图,你会看到我在Handler("流水线工人")那里写了个盖章二字,是什么意思呢?

一个handler通过post方法(我们下面再说,总是就是线程把这个消息Message丢给了Handler)拿到一个Message之后,这个Handler并不是马上处理这个消息,而是先盖上一个章:

[java] view plain copy
  1. msg.target=this;

然后把这个消息丢向流水线的"履带"——MessageQueue,让它排队去,等它排到队首之后,再通过msg.target.dispatchMessage()方法分到刚刚送它进流水线的“工人”手里进行处理。

有的同学可能会问了,Handler拿到Message为什么不马上处理呢?

原因是:本身Handler并没有提供并发解决机制,但MessageQueue的next()提供了并发解决机制。稍微理解一下这句话,很容易理解的。要知道Handler不只能接收到本线程丢过来的Msg包,还能接到其他线程(一般是子线程)丢过来的包(参考第一幅图),你不搞个基本法,排队来解决,这个程序怕是药丸。

这里还要注意一下那个Runnable callback的回调。现在先别管,总之先记着,我挖的坑我肯定会填的。

Message要注意的只有两种用法,一个是把信息封装,一个是解包提取信息;

[java] view plain copy
  1. Message msg = Message.obtain();//尽量不要一直new Message(),原因很简单,省内存。
  2. Bundle b=new Bundle();//信息封包
  3. b.putInt("Yidong",10086);
  4. msg.setData(b);
  5. //msg.sendToTarget();
  6. myHandler.post(msg);//先丢给流水线工人盖章、入列
[java] view plain copy
  1. Bundle b=msg.getData();//在handlerMessage()方法中解包提取信息。至于这个方法是啥,马上就要说到了。
  2. int a=b.getInt("Yidong");//a=10086

一句话总结,Message就是个"包裹",里面装了你需要传递信息,然后被丢来丢去(╯‵□′)╯︵,最后被分配到它的工人那里拆包进行处理。

3.Handler

终于,这篇文章要写完了。QAQ

接下来,我们要说我们的主角了——Handler了。大部分时候,你不需要关心MessageQueue和Looper是怎么工作的,但是Handler是你时时刻刻都必须打招呼的家伙。

首先是Handler究竟是个什么家伙。我把它类比为流水线上"工人" ,它即负责把收到的消息入列,也负责处理队首属于自己的那一份Message。

它的构造函数里没有啥东西,你只需要知道,它会先获取当前线程的Looper并绑定,然后从这个Looper获取到它的MessageQueue。然后,Handler与Looper、MessageQueue的关系就建立起来了。另外,他还创建了一个mCallback。这个待会儿再说。

刚刚我们一直在说msg.target.dispatchMessage()方法(其实也就是msg绑定的handler的disspatchMessage()),那么这个方法到底是个啥东西呢?看源码:

[java] view plain copy
  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }

第2行,看到那个callback了没有?是不是!很!熟!悉!我刚刚让你注意了有木有?还记得它是啥么?它其是个Runnable。

这一整段的代码就是告诉你:要分发这个代码,先看这个包裹的callback有没有绑定上啥东西(一个Runable);如果有,直接交由这个Runable进行处理,如果没有,查看当前handler的mCallback有没有绑定上啥东西;如果有,交由这个mCallback处理,如果没有,那么就调用当前handler的handleMessage()方法处理。

你可能要问,这个mCallback到底是个啥玩意?其实就是这个玩意:

[java] view plain copy
  1. public interface Callback {
  2. public boolean handleMessage(Message msg);
  3. }

讲白了就是这个handler"工人"的上一个"工人","包裹"Message被丢来丢去地传递,可能它真正要处理它的工人并不是当前给它盖章的工人,而是上一个.....不过,一般情况下我们并不会遇到这种情况。

现在要说的是handleMessage()方法,它的源码:

[java] view plain copy
  1. public void handleMessage(Message msg) {
  2. //填入你自己的方法
  3. }

卧槽?源码里是空的?

当然了,这个handleMessage()方法就是这个"包裹"Message被丢来丢去后最后要被进行处理的地方(被丢给Runable处理的不算),你当然要重写这个方法,给出你自己的处理方法了。

比如,把我上面写的那个解包提取信息的语句写进去......

一句话总结:

Handler是整个工作流水线的"传递者"和Message"包裹"的"分发者"(比如甩给Runnable)、"处理者",是流水线的"工人"。

好像,都写完了?

其实并没有,似乎,我们还有个Handler的post()方法没说。这个方法,就是线程把"包裹"丢给Handler,让Handler送其入列的方法。

4.Handler.post()

这个post到底有哪些方法呢?

[java] view plain copy
  1. public final boolean sendMessage(Message msg);//丢包法1,不吵吵直接丢
  2. public final boolean sendEmptyMessageDelayed(int what, long delayMillis);//丢包法2,延迟一段时间后丢一个空包,只含一个空的数据what=0,告诉Looper:“嘿哥们你还没空,继续转~”
  3. public final boolean sendMessageDelayed(Message msg, long delayMillis);//丢包法3,延迟一段时间后丢
  4. public boolean sendMessageAtTime(Message msg, long uptimeMillis);//丢包法4,在指定的时间丢

下面我会给出他们的源码,当然你要没啥兴趣读,也没关系,你可以直接看我给的结论,那就是

丢包法1中其实最后调用了丢包法2,2中调3,3中调4,4中调用了enqueueMessage(queue, msg, uptimeMillis),终于把这个包丢进了流水线"履带"MessageQueue。在enqueueMessage()方法中,会进行msg.target=this的操作,也就是我们刚刚说的"盖章"。

源码:

[java] view plain copy
  1. public final boolean sendMessage(Message msg){
  2. return sendMessageDelayed(msg, 0);
  3. }
  4. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  5. Message msg = Message.obtain();
  6. msg.what = what;
  7. return sendMessageDelayed(msg, delayMillis);
  8. }
  9. public final boolean sendMessageDelayed(Message msg, long delayMillis){
  10. if (delayMillis < 0) {
  11. delayMillis = 0;
  12. }
  13. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  14. }
  15. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  16. MessageQueue queue = mQueue;
  17. if (queue == null) {
  18. RuntimeException e = new RuntimeException(
  19. this + " sendMessageAtTime() called with no mQueue");
  20. Log.w("Looper", e.getMessage(), e);
  21. return false;
  22. }
  23. return enqueueMessage(queue, msg, uptimeMillis);
  24. }

这就是线程把信息"包裹"Message靠"工人"Message送入列的方法了。

别急,别忘了,handler其实还有个"分发者"的身份,把信息丢给Runnable处理的方法把?那么是怎么做的呢?其实一个Handler还有这个方法:

[java] view plain copy
  1. public final boolean post(Runnable r)
  2. public final boolean postDelayed(Runnable r, long delayMillis)
  3. public final boolean sendMessage(Message msg)
  4. public final boolean sendEmptyMessage(int what)

是的,其实也就是把mCallback的值变成这个Runnable而已罢了...

你可能要问,我一个好好的Runnable,怎么到了你Handler就变成一个Message了呢?

其实吧,Hander内部提供了getPostMessage方法把Runnable对象转化为Message:

[java] view plain copy
  1. private static Message getPostMessage(Runnable r) {
  2. Message m = Message.obtain();
  3. m.callback = r;
  4. return m;
  5. }

好的,到了这里,我们把这个利用Looper、MessageQueue、Handler进行消息传递的流程总结一下:

一个线程,首先进行Looper.prepare(),就可以创建出一个绑定了MessageQueue"履带"的[唯一]的Looper+MessageQueue"流水线";然后线程可以实例化出几个Handler"工人"。线程有要处理的信息"包裹"Message了,丢给对应的Handler"工人";这个工人判断一下这个到底是个Runnable还是Message,如果是Runnable就包装成一个Message,再"盖章",然后丢向流水线,让它排队;不过不是,"盖完章"不多bb直接甩进流水线。一个Message"包裹"到了流水线的队首,就要被拿出来,根据刚刚盖的章,各找各妈各回各家,该上哪上哪,然后进行msg.target.diapatchMessage()->msg.target.handleMessage()拆包处理。

看完这个总结,再去看我的图,是不是理解了?

恩,其实还有一点:你看我给的图的右边,还有个Thread 2,里面也站了一个Handler"工人",它负责把Thread 2要发给Thread 1的包裹丢进Thread 1的流水线。在编制上,他是Thread 1的"工人"(在Thread 1中实例化)。一般来说,Thread 2其实是 Thread 1的子线程。

为什么说是子线程呢?废话,要是Thread 1 在Thread 2 之前结束了,这名"工人"就被内存杀掉了,包要丢给谁?如果是子线程就放心,要么一起死,要么Thread 2死在 Thread 1之前。

Android 系统(18)---Handler,MessageQueue与Looper关系相关推荐

  1. 【Framework】透视Android中的Handler

    准备对基于Android应用开发Framework层的内容进行学习回顾,学习一个新技术前我们一般都会灵魂三问:What-Why-How(是什么.为什么.怎么用).源码的学习一定要亲自去看,用IDE或者 ...

  2. android版本内存多少,内存大小决定Android系统版本?

    随着Android系统版本的更新(从Android 1.x-4.x),Android手机的内存容量也在不断的翻番中.从最早的256MB到最新的3GB,12倍的数值变化在彰显科技的进步之余,也潜移默化地 ...

  3. Android系统开发之五:多线程编程详解(Handler ,Looper , Message , MessageQueue)

    本期的多线程主题与Android相关,侧重讲解在Android中如何用好多线程,需要你有Java的多线程基础. 首先我们思考几个问题,在Android应用中为什么要用多线程?为了解决哪些问题?或者为了 ...

  4. Android中的Handler, Looper, MessageQueue和Thread

    前几天,和同事探讨了一下Android中的消息机制,探究了消息的发送和接收过程以及与线程之间的关系.虽然我们经常使用这些基础的东西,但对于其内部原理的了解,能使我们更加容易.合理地架构系统,并避免一些 ...

  5. 正确理解 AsyncTask,Looper,Handler三者之间的关系(基于android 4.0)

    Looper 和Handler 是理解好AsyncTask的一个基础,我们可以先从这里开始,先给出一个主线程和子线程互相通信的例子. 1 package com.example.loopertest; ...

  6. Android消息机制(Handler、MessageQueue、Looper)详细介绍

    Android的消息机制其实在android的开发过程中指的也就是Handler的运行机制,这也就引出了android中常见的面试问题: 简述Handler.Looper.MessageQueue的含 ...

  7. Android 系统(59)---Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)

    Android开发:Handler异步通信机制全面解析(包含Looper.Message Queue) 前言 最近刚好在做关于异步通信的需求,那么,今天我们来讲解下Android开发中的Handler ...

  8. Android异步消息处理机制 深入理解Looper、Handler、Message三者关系

    转载子:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中 ...

  9. Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论. 1. 概述 ...

最新文章

  1. html js更改title,如何使用js改变HTML中title里面固定的文字
  2. 十大经典排序算法之冒泡排序及其优化
  3. 构造函数和clone以及在继承中
  4. 你可能被网帖骗了:中国粗离婚率的15连涨已在2018年被终结了
  5. Postgresql的一些命令
  6. jQuery UI 实现 仿购物车功能 简洁的js
  7. FTP 简介与 Windows 系统搭建 FTP 服务器
  8. SpringMVC一路总结(一)
  9. 管理感悟:出了事故,关键是想想自己哪里能改进
  10. oracle10g rac导出ocr,Oracle RAC OCR磁盘故障快速恢复方法
  11. Java线程并发协作与任务定时调度
  12. 计算机组成原理复习要点与考题类型--选择-填空-分析-计算-简答
  13. office 文档 在线查看
  14. rockchip研讨会_地下在线研讨会6
  15. ISO、光圈、曝光、焦距
  16. MDD | TO-252封装选型指南
  17. 无线定位技术实验一 TDOA-FDOA联合定位
  18. 参加全国大学生智能汽车竞赛,快来申请沁恒RISC-V MCU!
  19. 一步步教你做“锅打灰太狼”
  20. 过夫妻生活:50岁男人比30岁时更有魅力

热门文章

  1. ROS 教程之 vision : 用各种摄像头获取图像
  2. oracle主键id自动自增_Oracle主键ID设置自动增长(序列+触发器)
  3. 【JAVA SE】第六章 面向对象、对象和类以及封装
  4. 力扣171.Excel表列序号
  5. Spring Transaction 使用入门 (转)
  6. 小狼程序员:工作遐想
  7. 软件需求与分析课堂讨论
  8. AE开发 创建Feature后,需要进行拓扑检查
  9. Apache Qpid 认证绕过漏洞
  10. 如何检查列表是否为空?