真的了解Handler吗?

其实网上已经有很多关于Handler的文章,不管是源码方面的还是结构方面的,但如果只是看看别人理解的不是很深入,而且很快就没印象了。另外我最近在做一个功能的时候遇到了一些问题,一个View10秒平移到指定位置,View上面显示倒计时,这是个很简单的动画,但却出了一个很大的问题是倒计时的数字和View不同步,有时View已经结束了,但倒计时还剩1秒,倒计时的实现就是通过Handler的延迟发送消息实现的。解决这个问题其实很好解决可以使用Timer倒计时,也可以使用动画的差值器,根据差值器返回的数据计算当前的比率。

解决这个问题后我就在想我真的了解Handler吗?我发现我对它很陌生,我不知道它是怎么实现的,我只知道Handler、Looper、Message、MessageQueue大概都干什么,所以我就认真的看了下这4个类(Handler、Looper、Message、MessageQueue)的具体实现,真是不看不知道,一看吓一跳啊,和我以前理解的有不少不一样的地方,同时也学到了一些运用的思想,这里既是分享下我学习的心得,也是总结并加深对Handler的理解。

我个人不是很推荐直接就看具体实现,这里分享下我是如何看系统的源码(以Handler为例):
1、网上看看别人的总结,看了别人的总结你基本就对Handler的原理有个大概了解,基本知道每个类大概干了什么。
2、Handler几乎成了面试必问题,看看面试会问到哪些方面?带着问题去看源码:

  1. Handler是干什么?
  2. AsyncTask和Handler有什么区别
  3. Handler实现的基本原理
  4. 使用Handler的注意事项?为什么会造成内存泄漏?
  5. Message.obtain() 一定会获取缓存的对象吗?如何才能获取到缓存的对象?
  6. 在Activity中可以写多个handler,但这些消息都到一个队列,在执行的消息的时候如果知道执行哪个handler?
  7. handler.sendEmptyMessageDelayed(0,1000)
    handler.sendEmptyMessageDelayed(0,3000)
    这2个消息在MessageQueue中如何排列?谁先执行?
  8. handler.sendEmptyMessageDelayed(0,1000)一定会在1秒后执行吗?为什么?
  9. 同样发送上面2个消息,如果我把系统时钟调快2秒,消息如何执行?
  10. 子线程如何创建handler
  11. 如果想在Handler上添加优先级功能,如何实现?
  12. Handler目前是同步执行消息(导致消息执行不一定在指定的延迟执行),如果实现异步执行?优缺点?
  13. Handler 使用到了哪些模式?实际项目中是否用到了?

Handler

带着这些问题先看看Handler是如何实现的,在看源码时第一要看的是说明(注释),这里很好的说明了Handler干了什么、涉及到的相关类和使用说明。
在这里插入图片描述
第二个要看的是Structure

在这里插入图片描述
看代码最忌讳一行行的看,不要一看有1000多行就犯怵,看Structure主要是大概看下有哪些方法,通过函数名基本理解个差不多,以Handler这个为例:
在这里插入图片描述
这几个构造函数我们只需要看参数最全的一个就好,重点是理解这几个参数的作用:
在这里插入图片描述
mAsynchronous:默认false,如果设置为true:将会异步处理此消息,第12个问题知道如何实现了吗?
这几个参数在注释中有详细的说明,这里需要说说mCallback,
在这里插入图片描述
我们看下处理消息的逻辑,消息处理优先处理Message的callback(Message可以设置callback),如果为null则调用mCallback,最后才会处理重写handler的handleMessage方法,我们绝大部分是最后一种,那mCallback存在的意义是什么呢?我个人认为是为了写法上的不同,有了mCallback我们可以这样写:
在这里插入图片描述
我个人不是很喜欢这种写法,代码多了以后维护不是很方便。

下一套方法:
在这里插入图片描述
获取Message的一套方法,最终都是调用的都是Message.obtain()。

下个是发送消息的方法:
在这里插入图片描述
我们最常用的就是sendMessage(Message msg),这些方法就是组装Message,然后将Message插入到队列中,最终调用:enqueueMessage方法:
在这里插入图片描述
看这句代码:

msg.target = this;

把当前的handdler赋给了Message,在执行Message消息的时候将会调用:

msg.target.dispatchMessage()

然后调用Handler的handleMessage,就是我们重写的handleMessage方法。说到这里明白开始说的第6个问题了吗?如果我们的handler是以内部类的形式存在,那么handler存在当前Activity的强引用,handler又被Message引用,当我们想让activity销毁的时候,由于Message没有被执行,依然引用handler,handler引用activity,导致activity无法销毁,这就造成了内存泄漏。

最后在说下runWithScissors方法,这个方法呢反正我没用过,这是一个同步方法。

到这里Handler就看完了,别看它代码不少,其实总结起来主要的就3个功能,其他都是为这个3个函数服务的:

1、接收消息并将消息插入到消息队列,对应方法:enqueueMessage()
2、处理消息,对应方法:dispatchMessage()
3、同步处理消息,对应方法:runWithScissors()

Message

我们在看看Message,Message是消息的载体,就是我们经常写的实体bean,不过Message里面有一个享元模式的运用,我们通过Message.obtain()获取一个Message对象,这个对象会优先使用对象池中的对象。我们先看看它是如何实现的,先看下obtain方法:
在这里插入图片描述
首先注意一点这个方法是static,相关的属性也是static,这个很好理解,如果不是static的,那么消息池对应当前实例,没有意义。
这段代码很简单,就是判断当前消息池(sPool)是否为空,如果不为空说明有可用消息,那么就返回这个消息,如果为空,那就new一个消息返回,我们在看看这个属性sPool,发现这是Message对象

在这里插入图片描述
看到这里我们有个疑惑sPool怎么是Message对象呢?不应该是List集合吗?而且如果缓存的话不能仅仅缓存1个吧,其实我们仔细看看obtain方法就会发现其中的端倪,
在这里插入图片描述
如上面的红框中的代码,当sPool被返回后,sPool便会指向当前对象的next,说明消息池使用了单项链表,MessageQueue也是用了单项链表,有没有想过为什么不用List而用单项链表呢?如果我们知道List和单项链表的优缺点就明白了,链表对插入、移除的操作要比List效率高。
既然sPool是单项链表,那它是什么时候被赋值的呢?下面看下这个方法:
在这里插入图片描述

在调用Message的recycle方法时,会将当前对象放入消息池中,也就是说如果仅仅是调用Message.obtain()方法并不能得到消息池中的对象,因为消息池没有对象,只有在我们调用message.recycle()方法时才会在消息池中保存对象,那么问题又来了,我们也没有调用message.recycle()或者recycleUnchecked方法啊,那岂不是和new Message没有区别?错,其实系统帮我们调用了recycleUnchecked方法,是在Looper里面调用的。问题5是不是解决了呢。
Message到这里看完了,不过我要吐槽下几个方面:
在这里插入图片描述
Message有几个属性代表是否正在使用和是否同步,用boolean类型不好吗?
非要用与、或、位移来表示?谁能告诉这么写有什么好处吗?

Looper

Looper的作用就是循环获取消息并分发处理

public static void loop() {
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            ...
            try {
                msg.target.dispatchMessage(msg);
                ...
            } finally {
                ...
            }
            ...
            msg.recycleUnchecked();
        }
    }

loop方法的代码比较多,总结起来就上面3个步骤,

  1. 获取消息队列的下一条消息,queue.next()是阻塞方法
  2. 分发:msg.target.dispatchMessage(msg),这里就是调用handler的dispatchMessage方法,然后调用handleMessage方法。
  3. msg.recycleUnchecked(),回收,将消息存入消息池。

MessageQueue

消息队列主要干了2件事入队和出队,由于MessageQueue的实现涉及到jni层,这里就不具体分析具体代码了,假设如果让我们去实现这个类,我们如何实现呢?
这个类实现的功能需要下面几个需求:

  1. 单项链表
  2. 入队、出队2个方法的实现
  3. 根据when的大小确定位置(可选)

这个实现起来非常简单:
在这里插入图片描述

其实通过看Handler的源码学到了很多东西,Handler的实现是典型的生产者消费者模式,那我们以后遇到需要生产者消费者的功能时是不是就可以按照这套逻辑来实现了。

最近项目中要实现一个功能是收集App的日志并上传,这不就是典型的生产者消费者模式吗,所以学习思想真的很重要,有时几句代码不明白可以过,只要理解整体的设计逻辑即可。

发布了113 篇原创文章 · 获赞 66 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/mengks1987/article/details/86649059
今日推荐