为了账号安全,请及时绑定邮箱和手机立即绑定

Android 共享动画实现点击列表图片跳转查看大图页面

标签:
Android

    Handler是非常频繁使用于各种通信的一种工具,HandlerThread则是比较少用,但是二者之间具有一定的联系,今天来整理一下他们的大概知识点。现在我们先看看Handlder,了解之后再学习HandlerThread。


webp

Handler知识结构图

webp

HandlerThread知识结构图

目录大概如下:

    1.Handler是什么

    2.Handler的常规使用

    3.Handler的机制

    4.Handler的内存泄漏与解决方案

    5.使用Handler让子线程与子线程相互通信

    6.Handler的源码解析简要

    7.HandlerThread是什么

    8.HandlerThread的使用方法

    9.HandlerThread的机制原理

从以上内容,介绍Handler与HandlerThread的基本知识。

1.Handler是什么

    Handler是用于消息传送的一种异步机制工具,可以收放与处理Message、通过Runnable对象实现关联相关线程的MessageQueue。总的来说常用的功能有:

    (1)可以让对应的Message、Runnable在某个时间点得到处理

    (2)可以将耗时的操作放在子线程,然后发送接收消息,通知主线程进行UI更新

2.Handler的常规使用

    Handler():默认构造函数将此处理程序与Looper用于当前线程(Looper.myLooper,   因此如果是子线程,则需要在其之前Looper.prepare,为当前线程新建looper,在其构造初始化完成后启动循环,Looper.loop,详见参考书籍二第212页)。

    Handler(Handler.Callback callback):构造函数将此处理程序与Looper对于当前线程,并接受一个回调接口,您可以在其中处理消息。

    Handler(Looper looper):使用所提供的Looper而不是默认的。

    Handler(Looper looper, Handler.Callback callback):使用所提供的Looper而不是默认的,而是接受一个回调接口来处理消息。

  接下来我们就来看看Handler提供的各种方法吧:

    post(Runnable r):导致将Runnable r添加到消息队列中。

    postAtTime(Runnable r, long uptimeMillis):使Runnable r添加到消息队列,并在uptimeMillis.

    postDelayed(Runnable r, long delayMillis):使Runnable r被添加到消息队列,并在指定的时间流逝后运行。

    removeCallbacks(Runnable r):删除消息队列中所有可运行的Runnable消息任务。

    removeMessages(int what):删除消息队列中消息对象what字段为"what"的消息任务。

    sendEmptyMessage(int what):发送一个空消息对象,并设置这个空消息的what值。

    sendEmptyMessageAtTime(int what, long uptimeMillis):发送只包含要在特定时间传递的值的消息。

    sendEmptyMessageDelayed(int what, long delayMillis):发送一条消息,该消息只包含要在指定的时间间隔后传递的值。

    sendMessage(Message msg):将消息推送到消息队列的末尾,在当前时间之前完成所有挂起的消息。

    sendMessageAtTime(Message msg, long uptimeMillis):在所有挂起的消息在绝对时间之前uptimeMillis(以毫秒为单位)之前,将消息放入消息队列中。

    sendMessageDelayed(Message msg, long delayMillis):在所有挂起的消息之前(当前时间+delayMillis)之后,将消息放入消息队列中。

3.Handler的机制

    Handler机制是一个异步消息机制,由Handler、Message、MessageQueue、Looper四部分所组成,下面我们着重介绍一下这四个的介绍:

    (1)Handler:用于发送、接受、处理消息的处理者角色。详细地说就是用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理;

    (2)Message:消息,就是传递的数据封装对象,也可视为数据的载体;

    (3)MessageQueue:消息的队列,用于存放Handler所发送过来的消息,这些消息则在此等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象;

    (4)Looper:MessageQueue的管家,也就是消息队列的管理者,因为每个线程只有一个消息队列,因此每个线程也只有一个looper。一般通过Looper.prepare进行原线程looper的新建初始化,在调用Looper.loop()后则开启循环,从MessageQueue获取消息Message,然后获取其对应的handler,回调handler.dispatchMessage,进行消息传递与唤起消息处理(Handler对象的handleMessage方法),直到MessageQueue里的消息被全部取出跳出loop里面的循环;

    在这里补充一下,因为每个线程只能有一个Looper,也因此只能有一个MessageQueue。但是为什么每个线程只能有一个Looper呢?因为在源码里面,是通过静态对象sThreadLocal进行Looper的存储与集合管理(或者说用来保存Looper 对象),就相当于一个Map集合,键位当前的Thead线程,值为Looper对象。源码如下:

public final class Looper{

    private static final String TAG = "Looper";

   // sThreadLocal.get() will return null unless you've called prepare().

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

    final Thread mThread;

     private Printer mLogging;

    ...

    }

    从以上四个部分的角色介绍,我们再描述一下Handler的最常用场景进行描述一下:在UI线程进行handler的初始化,无论是匿名内部类还是其对应静态内部类、子类等都需要重写handleMessage方法,作为消息回调处理,通过参数msg来写接受消息过后UIi线程的逻辑处理的场所。之后,我们获取主线程的handler对象,传递给我们的子线程,在子线程内部进行数据获取/处理等耗时操作回调,将回应数据进行封装成msg,handler.sendMessage进行msg发送数据。至此,这是上层应用开发者所必须知晓的流程,接下来的步骤就是内部机制实现了;

    在sendMessage调用之后,msg则存放到对应looper的消息队列messageQueue中,等待被处理。在loop 启用后,则进行对消息队列循环获取消息msg,然后获取对应msg对应的handler,通过回调handler的dispatchMessage方法(分发msg)将消息传递给Handler的handleMessage方法。

    自此,整个handler的机制大致完成,可见下图:


webp

handler机制简图

4.handler的内存泄漏

原因:非静态内部类持有外部类的匿名引用,导致外部activity无法得到释放。

解决方法:handler内部持有外部的弱引用,并把handler改为静态内部类,在activity的onDestory()中调用handler的removeCallback()方法。

5.使用Handler让子线程与子线程相互通信

在handler初始化,之前进行该线程looper的新建与初始化Looper.prepare,之后进行调用loop。详细见参考博文

6.Handler的源码解析简要

    https://blog.csdn.net/u012827296/article/details/51236614

7.HandlerThread是什么

    先说一下需求,由于多次新建和关闭子线程都十分耗费资源,因此推出HandlerThread机制,来对应这类场景。本质上来说,Handler + Thread + Looper,是一个Thread内部有Looper,其继承于Thread。细化来看就是:

    (1)HandlerThread是继承于Thread,是一个线程类;

    (2)HandlerThread内部具有Looper对象,可以在自己内部进行loop循环;

    (3)通过Looper对象将消息传递给内部的Handler对象,可在handleMessage方法里面执行异步任务;

    (4)它的优点——不会阻塞,对性能消耗比较小;缺点——不能进行多任务的执行,需要等待处理,效率较低;

    (5)与多线程并发机制不同,它是(队列)串行方式,HandlerThread只有一个线程。

8.HandlerThread的使用方法

    HandlerThread最明显的就是支持了主线程对子线程的通信。其完整的通信过程应该是:主线程通知子线程,再由子线程通知主线程。对比来看传统的方式就是:在任务明确后进行子线程创建,然后子线程将结果通知给主线程。这样的好处就是,主线程可以告知子线程需要干什么,而且可以随时随地地进行消息通知,子线程在处理完对应任务后再通知主线程。当然,这样就不可避免了,你主线程本身还是需要一个Handler进行消息回调而更新UI。

    详细样例可见参考博文。

9.HandlerThread的机制原理

    HandlerThread在内部拥有一个Looper,并在run方法中进行创建,然后放入ThreadLocal里面,在调用loop方法唤起循环。Looper.loop()方法会不断循环从MessageQueue中取出消息处理消息,没有消息是则会阻塞。getLooper()方法是获取线程中的Looper对象,可以用来初始化Handler对象。 

    这里说一下  HandlerThread类中的quit、quitSafely方法区别,quit会清空队列里的消息(无论延时或者非延时),而quitSafely则只清空消息队列里的延时消息。当调用qiut或者quitSafely,Looper对象都不会再接收消息了,也就是说就停止消息的循环了。这时候再调用sendMessage或者post,都会返回false,线程也随即停止。



作者:双木火羽白
链接:https://www.jianshu.com/p/8b328512befa


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消