<
Handler学习总结
>
上一篇

Android Bitmap学习
下一篇

RecycleView学习

总结学习下Handler的发送消息和处理消息的源码实现

Handler学习总结

Handler使用的主要场景是子线程完成耗时操作的过程中,通过Handler向主线程发送消息Message,用来刷新UI界面,本次来总结学习下Handler的发送消息和处理消息的源码实现

我们的切入点从Handler的默认构造器开始。

一、从new Handler()开始

1

在无参构造器里调用了重载的构造方法并分别传入null和false。并且在构造方法中给两个全局变量赋值:mLooper和mQueue

这两者都是通过Looper来获取,具体如下:

2

可以看出,mLooper通过一个线程本地变量中的存根,然后mQueue是Looper中的一个全局变量,类型是MessageQueue类型

接下来具体分析重点就是这个Looper是什么?以及何时被初始化?

二、Looper介绍

思考一个问题,启动一个Java程序的入口函数是main方法,但是当main函数执行完之后此程序停止运行,也就是进程会自动终止。

但是当我们打开一个Activity后,只要不按下返回键Activity会一直显示在屏幕上,也就是Activity所在进程会一直处于运行状态 。实际上Looper内部维护一个无限循环,保证APP进程持续进行。

三、Looper初始化

我们了解过ActivityThread的main方法是一个新的APP进程的入口,具体实现如下:

3

解释说明:

prepareMainLooper方法如下:

4

图①处在prepareMainLooper中调用prepare方法创建Looper对象,仔细看发现其实就是new处一个Looper 。核心之处在于将new出的Looper设置到了线程本地变量sThreadLocal中。也就是说创建的Looper与当前线程发生了绑定

Looper的构造方法如下:

5

可以看出,在构造方法中初始化了消息队列MessageQueue对象。

prepare方法执行完之后,会在图③处调用myLooper()方法,从sThreadLocal中取出Looper对象并赋值给sMainLooper变量。

6

注意:

图②处,在创建Looper对象之前,会判断sThreadLocal中是否已经绑定过Looper对象,如果是则抛出异常。

是为了确保在一个线程中Looper.prepare()方法只能被调用1次。

如下,执行代码会立马报错:

7

注意:

不是说2次调用prepare才会抛出异常吗?其实在MainActivity所在进程被创建时,Looper的prepare方法已经在main方法中调用了1遍。这会导致一个非常重要的结果:

也就是说UI线程只会存在1个MessageQueue对象,后续我们通过Handler发送的消息都会被发送到这个MessageQueue中。

四、Looper负责做什么事情

一句话总结: 不断从MessageQueue中取出Message,然后处理Message中指定的任务

在ActivityThread的main方法中,除了调用Looper.prepareMainLooper初始化Looper对象之外,还调用了Looper.loop方法开启无限循环,Looper的主要功能就是在这个循环中完成的

8

很显然,loop方法中执行了一个死循环,这也是一个Android app进程能够持续运行的原因

图①处不断调用MessageQueue的next方法取出Message。如果message不为null则调用图②处进行后续处理。具体就是从Message中取出target对象,然后调用其dispatchMessage方法处理Message自身。

那这个target是谁呢?查看Message.java源码看到target就是Handler对象,如下:

9

Handler的dispatchMessage方法如下:

10

可以看出,在dispatchMessage方法中会调用一个空方法handleMessage,而这个方法也正是我们创建Handler时需要覆盖的方法。那么Handler是何时将其设置为一个Message的target的呢?

五、Handler的sendMessage方法

Handler有几个重载的sendMessage方法,但是基本都大同小异。我们用最普通的sendMessage方法来分析,代码具体如下:

11

可以看出经过几层调用之后,sendMessage最终会调用enqueueMessage方法将Message插入到消息队列MessageQueue中。而这个消息队列就是刚才分析的在ActivityThread的main方法中通过Looper创建的MessageQueue。

六、Handler的enqueueMessage方法

12

可以看出:

  1. 在图①处enqueueMessage方法中,将Handler自身设置为Message的target对象。因此后续Message会调用此Handler的dispatchMessage来处理
  2. 图②处会判断如果Message中的target没有被设置,则直接抛出异常
  3. 图③处会按照Message的事件when来有序的插入MessageQueue中,可以看出MessageQueue实际上是一个有序队列,只不过是按照Message的执行时间来排序

至此Handler的发送消息和消息处理流程已经介绍完毕,接下来看看常被问到的几个点:

七、相关问题

1、Handler的post(Runnable)和sendMessage区别?

首先看下post(Runnable)的源码实现:

13

实际上post(Runnable)会将Runnable赋值到Message的callback变量中,那么这个Runnable是在什么地方被执行的呢?Looper从MessageQueue中取出Message之后,会调用dispatchMessage方法进行处理,看下实现:

14

可以看出:

  1. 如果Message的Callback不为null,一般为通过post(Runnable)方式,会直接执行
  2. 如果Message的Callback为null,这种一般为sendMessage的方式,则会调用Handler的handlerMessage方法进行处理,这个就是我们复写的方法

2.Looper.loop()为啥不会阻塞主线程

刚才我们了解到,Looper中的loop方法实际上是一个死循环。但是我们的UI线程并没有被阻塞,反而还能进行各种手势操作,这是为啥?

在MessageQueue的next方法中,有如下一段代码:

15

nativePollOnce方法是一个native方法,当调用此native方法时,主线程会释放CPU资源进入休眠状态,直到下条消息到达或者唤醒主线程工作,这里采用epoll机制。

3.Handler的sendMessageDelayed或postDelayed如何实现?

前面提到过,在向MessageQueue队列中插入Message时,会根据Message的执行时间排序。而消息的延时处理的核心实现是在获取Message的阶段,看下MessageQueue的next方法:

16

一目了然了。图中蓝框处表示从MessageQueue中取出一个Message,但是当前的系统时间小于Message.when,因此会计算一个timeout时间段后再将UI线程唤醒,因此后续处理Message的代码只会在timeout时间之后才会被CPU执行。

注意!!!从上述代码可以看到,如果当前系统时间大于或等于Message.when ,那么会返回Message给Looper.loop()。但是这个逻辑只能保证在when之前消息不被处理,不能保证一定在when时被处理!!!

总结

Top
Foot