Android 04--Handler与HandlerThread

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


Handler知识结构图
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的机制大致完成,可见下图:


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,线程也随即停止。


---------------------

参考书籍:《Android开发艺术探索》、《疯狂Android讲义(第3版)》

参考博文:

作者:ClAndEllen  来源:CSDN   题目:Android面试系列文章2018之Android部分HandlerThread机制篇

链接:https://blog.csdn.net/ClAndEllen/article/details/79346492

---------------------

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容