转载

Handler 之 源码解析

上一篇博客Handler 之 初识使用中介绍了 Handler 的作用,以及 Handler 的基本用法,同时也详细介绍了为什么子线程不能更新 UI 的原因,但是因为篇幅原因,所以对 Handler 的内部机制并没有展开叙述。这篇文章就从 Handler 开始解析与之相关的源码,从而更好的理解 Handler 以及 Looper MessageQueue。

Handler 机制

写完上一篇文章,下面我该再读一遍 Handler 的源码了,其实讲 Handler 内部机制的博客已经很多了,但是自己还是要在看一遍,源码是最好的资料。

在具体看源码之前,有必要先理解一下 Handler、Looper、MessageQueue 以及 Message 他们的关系。

Handler Looper MessageQueue 之间的关系

Looper: 是一个消息轮训器,他有一个叫 loop() 的方法,用于启动一个死循环,不停的去轮训消息池。

MessageQueue: 就是上面说到的消息池

Handler: 用于发送消息,和处理消息

Message: 一个消息对象

现在要问了,他们是怎么关联起来的?

一切都要从 Handler 的构造方法开始。如下所示

final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; final boolean mAsynchronous;  public Handler(Callback callback, boolean async) {     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;   } 

可以看到 Handler 本身定义了一个 MessageQueue 对象 mQueue,和一个 Looper 的对象 mLooper。

不过,对 Handler 的这两个成员变量的初始化都是通过 Looper 才赋值的。

mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; 

现在,我们新建的 Handler 就和 Looper、MessageQueue 关联了起来,而且他们是一对一的关系,一个 Handler 对应一个 Looper,同时对应一个 MessageQueue 对象。这里给 MessageQueue 的赋值比较特殊,

mQueue = mLooper.mQueue; 

这里直接使用 looper 的 mQueue 对象,将 Looper 的 mQueue 赋值给了 Hander 自己,现在 Looper 和 Handler 持有着同一个 MessageQueue 。

这里可以看到 Looper 的重要性,现在 Handler 中的 Looper 实例和 MessageQueue 实例都是通过 Looper 来完成设置的,那么下面我们具体看看 Looper 是怎么实例化的,以及他的 mQueue 是怎么来的。

Looper

从上面 Handler 的构造方法中可以看到,Handler 的 mLooper 是这样被赋值的。

mLooper = Looper.myLooper(); 

接着看 myLooper() 的实现。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  public static @Nullable Looper myLooper() {     return sThreadLocal.get(); } 

这里出现了一个平时不怎么看到的 ThreadLocal 类,关于这个类,推荐去阅读任玉刚的一篇文章 - Android的消息机制之ThreadLocal的工作原理,讲的很不错。另外自己也写了一篇文章,用于讲解 ThreadLocal 的用法,以及他在 Handler 和 Looper 中的巧妙意义

任玉刚 Android的消息机制之ThreadLocal的工作原理

咕咚 Handler 之 ThreadLocal 相关

这里他是通过 ThreadLocal 的 get 方法获得,很奇怪,之前我们没有在任何地方对 sThreadLocal 执行过 set 操作。 这里却直接执行 get 操作,返回的结果必然为空啊!

但是如果现在为空,我们在 new Handler() 时,程序就已经挂掉了啊,因为在 Handler 的构造方法中,如果执行 Looper.myLooper() 的返回结果为空。

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

但是,我们的程序没有挂掉,这意味着,我们在执行 myLooper() 方法时,他返回的结果不为空。

为什么呢?那我们在 Looper 中看看,哪里有对应的 set 方法,如下所示,我们找到了一个全局静态方法 prepare

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");     }     sThreadLocal.set(new Looper(quitAllowed)); } 

我们看到,在最后一行这里,执行了对应的 set 操作,这里把一个 new 出来的 Looper 直接 set 到 sThreadLocal 中。

但是我们不知道,到底什么时候,是谁调用了 prepare() 方法,从而给 sThreadLocal 设置了一个 Looper 对象。

后来在网上经过搜索,找到了答案,我们的 Android 应用在启动时,会执行到 ActivityThread 类的 main 方法,就和我们以前写的 java 控制台程序一样,其实 ActivityThread 的 main 方法就是一个应用启动的入口。

在这个入口里,会做很多初始化的操作。其中就有 Looper 相关的设置,代码如下

public static void main(String[] args) {      //............. 无关代码...............      Looper.prepareMainLooper();      Looper.loop();      throw new RuntimeException("Main thread loop unexpectedly exited"); } 

在这里,我们很清楚的看到,程序启动时,首先执行 Looper.prepareMainLooper() 方法,接着执行了 loop() 方法。

先看看 prepareMainLooper 方法。

public static void prepareMainLooper() {     prepare(false);     synchronized (Looper.class) {         if (sMainLooper != null) {             throw new IllegalStateException("The main Looper has already been prepared.");         }         sMainLooper = myLooper();     } }  public static @Nullable Looper myLooper() {     return sThreadLocal.get(); }  private static void prepare(boolean quitAllowed) {   if (sThreadLocal.get() != null) {       throw new RuntimeException("Only one Looper may be created per thread");   }   sThreadLocal.set(new Looper(quitAllowed)); } 

这里,首先调用了 prepare() 执行完 prepare(),sThreadLocal 成功绑定了一个 new Looper() 对象,然后执行

sMainLooper = myLooper(); 

可以看看 sMainLooper 的定义,以及 myLooper() 方法;

private static Looper sMainLooper;  // guarded by Looper.class      public static @Nullable Looper myLooper() {     return sThreadLocal.get(); } 

现在的 sMainLooper 就有值了,也就是说,只要我们的 App 启动,sMainLooper 中就已经设置了一个 Looper 对象。以后调用 sMainLooper 的 get 方法将返回在程序启动时设置的 Looper,不会为空的。

下面在看 main 方法中的 调用的 Looper.loop() 方法。 已经把一些无关代码删了,否则太长了,

public static void loop() {      //获得一个 Looper 对象     final Looper me = myLooper();      // 拿到 looper 对应的 mQueue 对象     final MessageQueue queue = me.mQueue;      //死循环监听(如果没有消息变化,他不会工作的) 不断轮训 queue 中的 Message     for (;;) {         // 通过 queue 的 next 方法拿到一个 Message         Message msg = queue.next(); // might block         //空判断         if (msg == null)return;         //消息分发            msg.target.dispatchMessage(msg);         //回收操作           msg.recycleUnchecked();     } } 

现在,想一个简单的过程,我们创建了一个 App,什么也不做,就是一个 HelloWorld 的 Android 应用, 此时,你启动程序,即使什么也不干,按照上面的代码,你应该知道的是,现在的程序中已经有一个 Looper 存在了。 并且还启动了消息轮训。 Looper.loop();

但是,目前来看,他们好像没什么用,只是存在而已。

此时你的项目如果使用了 Handler,你在主线程 new 这个 Handler 时,执行构造方法

public Handler(Callback callback, boolean async) {     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; } 

此时的 myLooper() 返回的 Looper 就是应用启动时的那个 Looper 对象,我们从 Looper 的构造方法得知,在 new Looper 时,会新建一个对应 的消息池对象 MessageQueue

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

那么在 Handler 的构造方法中,那个 mQueue 其实也是在应用启动时就已经创建好了。

现在再来回顾一下 Handler 的构造方法,在构造方法中,他为自己的 mQuery 和 mLooper 分别赋值,而这两个值其实在应用启动时,就已经初始化好了。

并且,现在已经启动了一个消息轮训,在监听 mQuery 中是不是有新的 Message !

现在这个轮训器已经好了,我们看发送消息的过程。

SendMessage

我们使用 Handler 发送消息

mHandler.sendMessage(msg);

Handler 有好多相关的发送消息的方法。但是追踪源码,发现他们最终都来到了 Handler 的这个方法 sendMessageAtTime

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() 方法体

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {       msg.target = this;       // 使用默认的 handler 构造方法时,mAsynchronous 为 false。       if (mAsynchronous) {           msg.setAsynchronous(true);       }       return queue.enqueueMessage(msg, uptimeMillis);     } 

这里有一句至关重要的代码

msg.target = this; 

我们看看 msg 的 target 是怎么声明的

Handler target; 

意思就是每个 Message 都有一个类型为 Handler 的 target 对象,这里在 handler 发送消息的时候,最终执行到 上面的方法 enqueueMessage() 时,会自动把当前执行 sendMessage() 的 handler对象,赋值给 Message 的 target。

也就是说,Handler 发送了 Message,并且这个 Message 的 target 就是这个 Handler。

想想为什么要这么做?

这里再说一下,Handler 的作用,

  • 发送消息
  • 处理消息

先不看代码,我们可以想想,handler 发送了 message ,最终这个 message 会被发送到 MessageQueue 这个消息队列。 那么最终,谁会去处理这个消息。

在这里消息发送和处理遵循『谁发送,谁处理』的原则。

现在问题来了,就按照上面说的,谁发送,谁处理,那现在应该是 handler 自己处理了,但是他在哪里处理呢?到现在我们也没看到啊。

慢慢来,接下来继续看消息的传递。

现在,我们只要发送了消息,那么消息池 mQuery 就会增加一个消息,Looper 就会开始工作,之前已经说了,在应用启动的时候, 已经启动了 Looper 的 loop() 方法,这个方法会不断的去轮训 mQuery 消息池,只要有消息,它就会取出消息,并处理,那他是怎么处理的呢?看一下 loop() 的代码再说。

public static void loop() {      //获得一个 Looper 对象     final Looper me = myLooper();      // 拿到 looper 对应的 mQueue 对象     final MessageQueue queue = me.mQueue;      //死循环监听(如果没有消息变化,他不会工作的) 不断轮训 queue 中的 Message     for (;;) {         // 通过 queue 的 next 方法拿到一个 Message         Message msg = queue.next(); // might block         //空判断         if (msg == null)return;         //消息分发            msg.target.dispatchMessage(msg);         //回收操作           msg.recycleUnchecked();     } } 

看 for()循环,他在拿到消息后,发现 msg 不为空,接着就会执行下面这句非常重要的代码

msg.target.dispatchMessage(msg); 

这里执行了 msg.target 的方法 dispatchMessage,上面已经在 sendMessage 时看到了,我们在发送消息时,会把 msg 的 target 设置为 handler 本身,也就是说,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);     } } 

我们看到,我们没有给 msg 设置 callback 也没有给 handler 的 mCallback 设置过值,所以此时,会执行 handleMessage() 方法;

public void handleMessage(Message msg) {  } 

发现这是一个空方法,所以自己的新建 Handler 时,只要复写了这个方法,我们就可以接受到从子线程中发送过来的消息了 。

在看一遍自己定义 Handler 时,如何定义的,如何复写 handlerMessage

private Handler mHandler = new Handler(){      @Override      public void handleMessage(Message msg) {          super.handleMessage(msg);          switch (msg.what){              case 1:                  Bitmap bitmap = (Bitmap) msg.obj;                  imageView.setImageBitmap(bitmap);                  break;              case -1:                  Toast.makeText(MainActivity.this, "msg "+msg.obj.toString(), Toast.LENGTH_SHORT).show();                  break;          }      }  }; 

在这里,我们处理了自己发送的消息,到此 Handler 的内部机制大体就分析完了。

但是从上面的 dispatchMessage 方法我们也能看出,Handler 在处理消息时的顺序是什么?

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

他首先判断 Message 对象的 callback 对象是不是为空,如果不为空,就直接调用 handleCallback 方法,并把 msg 对象传递过去,这样消息就被处理了。

如果在发送消息时,我们没有给 Message 设置 callback 对象,那么程序会执行到 else 语句块,此时首先判断 Handler 的 mCallBack 对象是不是空的,如果不为空,直接调用 mCallback 的 handleMessage 方法进行消息处理。

最终,只有当 Handler 的 mCallback 对象为空,才会执行自己的 handleMessage 方法。

这是整个处理消息的流程。

现在就会想了,在处理消息时,什么时候才能执行到第一种情况呢,也就是通过 Message 的 callback 对象处理。

其实简单,查看源码发现

/*package*/ Runnable callback; 

callback 是一个 Runnable 接口,那我们这怎么才能设置 Message 的 callback 的参数呢?最后观察发现,Handler 发送消息时,除了使用 sendMessage 方法,还可以使用一个叫 post 的方法,而他的形参正好就是 Runnable,我们赶紧拔出他的源码看看。

public final boolean post(Runnable r) {    return  sendMessageDelayed(getPostMessage(r), 0); } 

接着看 getPostMessage() 这个方法。

private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }

代码看到这里,已经很清楚了,getPostMessage() 返回了一个 Message 对象,这个对象中设置了刚才传递过来的 runnable 对象。

到这里,你应该明白了,在处理消息时,除了 Handler 自身的 handlerMessage() 方法设置处理,还可以直接在发消息时指定一个 runnable 对象用于处理消息。

另外上面通过 dispatchMessage() 的代码已经看出来,处理消息有三种情形,第一种直接使用 Message 的 running 对象处理,如果不行使用第二种 用 Handler 的 mCallback 对象处理,最后才考虑使用 handlerMessage 处理,关于第二种情形,这里就不分析了,自己试着看代码应该能找到。Good luck ~

原文  http://gudong.name/technology/2016/03/10/handler_analysis_two.html
正文到此结束
Loading...