详解Android中Handler的内部实现原理

作者:孙群 时间:2023-05-11 12:50:12 

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文《详解Android中Handler的使用方法》里面对Android为何以引入Handler机制以及如何使用Handler做了讲解。

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

详解Android中Handler的内部实现原理

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

一图胜千言

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

详解Android中Handler的内部实现原理

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

我们可以把传送带上的货物看做是一个个的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代码如下:


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方法。

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

详解Android中Handler的内部实现原理

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

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


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

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


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方法之间的调用关系:

详解Android中Handler的内部实现原理

通过分别分析sendEmptyMessageXXX、postXXX方法与sendMessageXXX方法之间的关系,我们可以看到在Handler中所有可以直接或间接向消息队列发送Message的方法最终都调用了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;
 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放入到消息队列中。

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

详解Android中Handler的内部实现原理

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


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源码如下:


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。

希望本文对于大家理解Android中的Handler和消息循环机制有所帮助。

标签:Android,Handler
0
投稿

猜你喜欢

  • java实现在原有日期时间上加几个月或几天

    2023-08-06 16:39:48
  • Java之BigDecimal的坑及解决

    2022-05-17 01:09:01
  • Spring容器-BeanFactory和ApplicationContext使用详解

    2022-03-20 07:02:45
  • Netty分布式高性能工具类recycler的使用及创建

    2022-03-04 17:57:32
  • C#开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨

    2023-03-28 14:49:53
  • Spring 代理 Bean 获取不到原始 Bean 对象注解解决方法

    2022-10-31 17:06:08
  • C#获取局域网MAC地址的简单实例

    2022-08-04 11:26:12
  • Kotlin中空判断与问号和感叹号标识符使用方法

    2022-11-12 13:48:04
  • Java中抽象类和接口介绍

    2022-05-28 02:42:09
  • JAVA布局管理器与面板组合代码实例

    2022-04-23 15:48:55
  • Android中 视频屏幕左半部分上下滑动改变亮度右半部分上下滑动改变声音

    2021-08-13 02:57:12
  • wpf将表中数据显示到datagrid示例

    2023-06-13 04:14:06
  • Unity时间戳的使用方法

    2023-11-11 05:19:00
  • Android scrollToTop实现点击回到顶部(兼容PullTorefreshScrollview)

    2021-07-29 09:06:37
  • java-制表符\\t的使用说明

    2023-12-03 10:18:02
  • Android自定义评分控件的完整实例

    2021-10-26 19:25:58
  • VSCODE+cmake配置C++开发环境的实现步骤

    2023-05-31 21:25:14
  • Java类库BeanUtils组件使用方法及实例详解

    2022-09-28 00:37:49
  • Intellij IDEA 2019 最新乱码问题及解决必杀技(必看篇)

    2023-12-04 18:30:02
  • Spring MVC全局异常实例详解

    2021-11-19 13:51:26
  • asp之家 软件编程 m.aspxhome.com