前言:基于Android 31阅读源码 @Deprecated

1:Handler的常见用法:

1.1:主线程实现方式

/*** 定义静态内部类实现Handler* 从Android 11 起,Handler 被标注为:@Deprecated*/
class MyStaticHandler(activity: HandlerActivity) : Handler() {private var weakReference: WeakReference<HandlerActivity>? = nullinit {weakReference = WeakReference(activity)}override fun handleMessage(msg: Message) {super.handleMessage(msg)weakReference?.get()?.let {Log.e(it.TAG, "handleMessage: what: ${msg.what}")}}
}//调用实现发送消息:
//主线程发送消息
val myHandle: MyStaticHandler = MyStaticHandler(this@HandlerActivity)
val message = myHandle.obtainMessage(11)
myHandle.sendMessage(message)

1.2:协程内实现方式

var threadHandler: Handler? = null//协程内Handler接收处理消息
CoroutineScope(Dispatchers.Default).launch {Looper.prepare()threadHandler = @SuppressLint("HandlerLeak")object : Handler() {override fun handleMessage(msg: Message) {super.handleMessage(msg)Log.e(TAG, "handleMessage: .what==${msg.what} in Thread.name:${Thread.currentThread().name}")Looper.myLooper()?.quitSafely()//消息处理完成后,退出Looper 不在堵塞子线程,释放}}Looper.loop()}//协程内发送消息
CoroutineScope(Dispatchers.Default).launch {threadHandler?.sendEmptyMessage(55)
}

2、Handler发送消息的几种方式,主要是 send 和 post 的api上

//主线程发送消息
val myHandle: MyStaticHandler = MyStaticHandler(this@HandlerActivity)
val message = myHandle.obtainMessage(11)
myHandle.sendMessage(message)myHandle.sendMessage(Message().apply { what = 22 })
myHandle.sendEmptyMessage(33)
myHandle.sendEmptyMessageDelayed(44,3000)
myHandle.sendMessageAtFrontOfQueue(Message().apply { what = 55 })
myHandle.post {Log.e(TAG, "callback-run: ", )
}
//等等其它几个api

3、Handler 三要素:Looper、Message、MessageQueue

3.1:Looper:

实例化 Handler 的时候 Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,也就是说在创建 Handler 之前一定需要先创建 Looper 。源码如下

public Handler(@Nullable Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}mLooper = Looper.myLooper();//TODO:如果looper 为null,则爆出异常Exceptionif (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}

在常规的主线程内使用 Handler 一般是看不见这个过程的,因为系统源码已经帮我们处理并实现了,Looper 提供了 Looper.prepare()  方法来创建 Looper ,并且会借助 sThreadLocal 来实现与当前线程的绑定功能。Looper.loop() 则会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler

private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));
}public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);me.mSlowDeliveryDetected = false;for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {return;}}
}

扩展:可以看到 Looper是和当前线程绑定在一起,借此我们可以通过 Looper判断当前线程是否为主线程:Looper.getMainLooper() == Looper.myLooper()

3.2、Message,这是Handler要发送的消息实体,其中最关键的是 target 变量,他所保存的正式:发送消息的Handler

3.3、MessageQueue:管理Message 的队列

小结:

Handler:用来发送和接收处理消息;

Message:消息的实体,其中target保存发送消息的Handler;

MessageQueue:用于存储和管理Message的队列;

Looper: 用于和当前线程进行绑定,并通过Looper.loop() 开启无限循环的从MessageQueue队列内取消息,通过取出的Message 利用变量target 发送给Handler 接收处理消息

面试中常见的问题:

1、Handler 是如何与线程关联的?

Handler 创建之初都会去检查 Looper是否为null , 如果为null 则爆异常信息;Looper是通过Looper.prepare()和当前线程进行绑定关联,涉及到ThreadLocal

2、Handler 发出去的消息是谁管理的?

handler发送消息都是通过send 或 post  一系列api 进行消息处理,无论通过那个api最后都会调用

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) ,然后入队列:queue.enqueueMessage(msg, uptimeMillis);也就是由MessageQueue进行统一管理

3、消息又是怎么回到 handleMessage() 方法的?

问题 2 中在调用enqueueMessage 函数的时候,将this赋值给了Message.target变量,也就是每个Message都携带者发送者的信息,this; 那么通过 Looper.loop()取到Message后利用target 又送回到发送Handler 的回调函数handlerMessage()方法

4、线程的切换是怎么回事?

上面问题 1 说明了Handler是如何与线程A关联的,那么关联之后,Handler.handlerMessger就在此线程执行,也就是在 子线程B 内调用handler 发送消息,那么处理消息都会回到线程A内,也就是所谓的线程切换。

5、Handler引起的内存泄漏原因和最佳方案

Handler 允许我们发送延时信息,如果在延时期内关闭了Activity,此时会导致内存泄漏,最佳的处理方案是:采用静态内部类 + 弱引用的方式 如上 MyStaticHandler 实现,并且追加一下onDestory 的销毁操作:

override fun onDestroy() {super.onDestroy()myHandle?.removeCallbacksAndMessages(null)
}

6、Handler 里藏着的 Callback 能干什么?

这个涉及到 Looper.loop() 函数的无限循环取Message值,并处理分发这些消息,如下源码

public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

源码可见,如果Message的callback不为null, 优先处理handlerCallback(msg)

示例:

CoroutineScope(Dispatchers.Default).launch {threadHandler?.post(Runnable {Log.e(TAG, "loadData: callback.run")})}//打印日志:E/HandlerActivity: loadData: callback.run

说明,对于Message.callback优先处理,不在返回Handler.handlerMessage处理

7、创建Message的最佳实现:

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。

方法有二:

  1. 通过 Message 的静态方法 Message.obtain();   获取;
  2. 通过 Handler 的公有方法 handler.obtainMessage(); 。

8、子线程里弹 Toast 的正确姿势

Thread() {kotlin.run {Looper.prepare()Toast.makeText(this, "子线程 Toast", Toast.LENGTH_LONG).show()Looper.loop()}
}.start()

9、一个Thread可以有几个Looper?几个Handler

一个线程只能有一个Looper,可以有多个Handler,在线程中我们需要调用Looper.perpare,他会创建一个Looper并且将Looper保存在ThreadLocal中,每个线程都有一个LocalThreadMap,会将Looper保存在对应线程中的LocalThreadMap,key为ThreadLocal,value为Looper

10、可以在子线程直接new一个Handler吗?那该怎么做?

可以在子线程中创建Handler,我们需要调用Looper.perpare和Looper.loop方法。或者通过获取主线程的looper来创建Handler

11、Message可以如何创建?哪种效果更好,为什么?

Message.obtain来创建Message。这样会复用之前的Message的内存,不会频繁的创建对象,导致内存抖动。

12、使用Hanlder的postDealy()后消息队列会发生什么变化?

Handler发送消息到消息队列,消息队列是一个时间优先级队列,内部是一个单向链表。发动postDelay之后会将该消息进行时间排序存放到消息队列中

13、点击页面上的按钮后更新TextView的内容,谈谈你的理解?(阿里面试题)]

点击按钮的时候会发送消息到Handler,但是为了保证优先执行,会加一个标记异步,同时会发送一个target为null的消息,这样在使用消息队列的next获取消息的时候,如果发现消息的target为null,那么会遍历消息队列将有异步标记的消息获取出来优先执行,执行完之后会将target为null的消息移除。(同步屏障:Handler机制——同步屏障_start_mao的博客-CSDN博客_同步屏障)

14、Handler是如何完成子线程和主线程通信的?

在主线程中创建Handler,在子线程中发送消息,放入到MessageQueue中,通过Looper.loop取出消息进行执行handleMessage,由于looper我们是在主线程初始化的,在初始化looper的时候会创建消息队列,所以消息是在主线程被执行的。

15、关于ThreadLocal,谈谈你的理解?

ThreadLocal类似于每个线程有一个单独的内存空间,不共享,ThreadLocal在set的时候会将数据存入对应线程的ThreadLocalMap中,key=ThreadLocal,value=值

16、享元设计模式有用到吗?

享元设计模式就是重复利用内存空间,减少对象的创建,Message中使用到了享元设计模式。内部维护了一个链表,并且最大长度是50,当消息处理完之后会将消息内的属性设置为空,并且插入到链表的头部,使用obtain创建的Message会从头部获取空的Message

17、Handler异步消息处理(HandlerThread)

内部使用了Handler+Thread,并且处理了getLooper的并发问题。如果获取Looper的时候发现Looper还没创建,则wait,等待looper创建了之后在notify

18、子线程中维护的Looper,消息队列无消息的时候处理方案是什么?有什么用?

子线程中创建了Looper,当没有消息的时候子线程将会被block,无法被回收,所以我们需要手动调用quit方法将消息删除并且唤醒looper,然后next方法返回null退出loop

19、既然可以存在多个Handler往MessageQueue中添加数据(发消息是各个handler可能处于不同线程),那他内部是如何确保线程安全的?

在添加数据和执行next的时候都加了this锁,这样可以保证添加的位置是正确的,获取的也会是最前面的。

20、关于IntentService,谈谈你的理解

HandlerThread+Service实现,可以实现Service在子线程中执行耗时操作,并且执行完耗时操作时候会将自己stop。

21、Looper.loop()是一个无限循环,为什么主线不会被卡死

参考:Handler 都没搞懂,拿什么去跳槽啊? - 掘金

Android中为什么主线程不会因为Looper.loop()里的死循环卡死? - 知乎

Android消息机制1-Handler(Java层) - Gityuan博客 | 袁辉辉的技术博客

Android | 面试必问的 Handler,你确定不看看? - 简书

享元模式 | 菜鸟教程

Handler  |  Android Developers

消息通信--Handler相关推荐

  1. 【Android】线程间通信——Handler消息机制

    文章目录 引言 Java层 永动机跑起来 示例 Looper Handler MessageQueue 永动机停下 Native层 nativeInit() nativePollOnce() nati ...

  2. java 进程通信框架,MediatR-进程内的消息通信框架

    MediatR是一款进程内的消息订阅.发布框架,提供了Send方法用于发布到单个处理程序.Publish方法发布到多个处理程序,使用起来非常方便.目前支持 .NET Framework4.5..NET ...

  3. Android消息通信之无所不能的第三方开源项目EventBus

     Android消息通信之无所不能的第三方开源项目EventBus 在Android开发中,消息通信在开发过程中是比较重要但比较略微繁琐的过程,比如,Activity与Fragment之间的消息通 ...

  4. Android 消息机制 Handler总结

    老久就想着写一篇 关于消息机制的文章来总结一下. Android的消息机制主要是指Handler 的运行机制.我们在开发时有的时候需要在子线程进行耗时的I/o 操作,可能是读取文件或者 访问网络等,有 ...

  5. 线程间通信: Handler , Looper, MessageQueue, Message (完结)

    概述:    为了 线程间 通信方便, Handler 机制 通过 Handler 和 Looper, MessageQueue, Message 这些 类 之间的协作, 简化 多线程的开发.  线程 ...

  6. android 手机内存uri_Android消息机制Handler原理解析

    关注[搜狐技术产品]公众号,第一时间获取技术干货 导读 在Android中,Handler一直是一个热点,在开发过程中,它的使用频率很高,而且在Android源码中Handler都是常客.那么Hand ...

  7. Android消息机制Handler用法

    这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...

  8. 最重要的事情 一 、消息通信机制(1)ant 打包方法(2) system.out.println()用法 二、UML学习

    最重要的事情    一 .消息通信机制(1)ant 打包方法(2) system.out.println()用法      二.UML学习

  9. Spark源码阅读02-Spark核心原理之消息通信原理

    Spark消息通信架构 在Spark中定义了通信框架接口,这些接口实现中调用了Netty的具体方法.通信框架使用了工厂设计模式,这种模式实现了对Netty的解耦,能够根据需要引入其他的消息通信工具. ...

最新文章

  1. android存储器,Android——寄存器和存储器的区别
  2. 独家 | 浅析机器学习中的自由度
  3. hdfs mv命令_大数据入门:HDFS文件管理系统简介
  4. 前端开发时间格式的转换方法_开发人员投资时间而不浪费时间的10种方法
  5. AP 1532E register   Cisco 2504 AP注册WLC
  6. python声明编码_Python 2.x 编码声明:是coding:utf-8还是coding=urf-8呢
  7. java四舍五入自己写_java提高篇-----详解java的四舍五入与保留位
  8. AUTOCAD圆角半径过大问题的思考和计算
  9. 计算机论文的研究思路与方法,计算机毕业论文开题报告教学网站的设计与实现...
  10. 读取excel数据批量填充world
  11. FPGA的NIOS-II
  12. Ubuntu18.04 安装花生壳并使用
  13. null与undefined的异同点
  14. 我的项目: UConn summer academy BI制药 缺失值处理
  15. Linux--命名管道(FIFO)
  16. html360全景图原理,html360°全景展示 示例
  17. 用AI从零开始创建一个宫崎骏的世界
  18. python 声音基频f0_ASR中常用的语音特征之FBank和MFCC(原理 + Python实现)
  19. 如何将项目部署到服务器:从选择服务器到维护应用程序的全流程指南
  20. oracle rac 宕机频繁,Oracle rac宕机分析故障处理

热门文章

  1. Linux系统编程——网络编程
  2. RDE-基于若依框架的部门组织架构导出项目
  3. 微信8.0.6内测版本更新啦,这次又“炸”了(附内测地址)
  4. springboot中返回前端空对象或空集合处理实现
  5. arc hdmi 接线图_HDMI ARC功能详解及应用介绍
  6. 数据库中sum的详细用法介绍
  7. 友盟推送android 8.0系统不显示推送消息问题
  8. 三维地图3D可视化应用案例
  9. 关于肌电、脑电的期刊记录
  10. Golang 包的定义、引入和工程管理