Android面试必问之Handler机制

article/2025/11/6 1:13:56

Android面试必问之Handler机制

  • 1. 作用
  • 2. 基本使用
    • 2.1 创建Handler实例
    • 2.2 发送消息
    • 2.3 使用post方法
    • 2.4 使用sendMessage方法
    • 2.5 通过Message与Handler进行通信的步骤
  • 3. 源码分析
    • 3.1 为什么Handler能够切换线程执行?
    • 3.2 Handler.post(Runnable) 方法是运行在新的线程吗?
    • 3.3 Handler(Callback) 跟 Handler() 这两个构造方法的区别在哪?
    • 3.4 子线程可以创建 Handler 吗?
    • 3.5 为什么主线程不用调用 Looper.prepare() ?
    • 3.6 为什么创建 Message 对象推荐使用 Message.obtain()获取?
    • 3.7 梳理
  • 4. 常见问题&技巧
    • 4.1 为什么 Handler 会造成内存泄漏?
    • 4.2 怎么防止 Handler 内存泄漏?
    • 4.3 Looper.loop() 为什么不会造成应用卡死?
  • 5. 总结

参考:https://www.jianshu.com/p/13c8a66d3b5c
https://pqpo.me/2017/05/03/learn-messagequeue/

1. 作用

Handler是一种用于线程间的消息传递机制。
因为 Android 中不允许在非主线程更新UI,所以最常使用的地方就是用于子线程获取某些数据后进行UI的更新。

2. 基本使用

2.1 创建Handler实例

//1.自定义Handler类
static class CustomHandler extends Handler{@Overridepublic void handleMessage(Message msg) {//更新UI等操作}
}CustomHandler customHandler = new CustomHandler();//2.内部类
Handler innerHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//更新UI等操作}
};//3.callback方式
Handler callbackHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {//更新UI等操作return true;}
});

2.2 发送消息

 //1.发送普通消息Message msg = Message.obtain();msg.what = 0; //标识msg.obj = "这是消息体"; //消息内容innerHandler.sendMessage(msg);//2.发送Runnale消息innerHandler.post(new Runnable() {@Overridepublic void run() {//更新UI等操作,消息接收后执行此方法}});

2.3 使用post方法

在这里插入图片描述

2.4 使用sendMessage方法

在这里插入图片描述
在这里插入图片描述

2.5 通过Message与Handler进行通信的步骤

在这里插入图片描述

3. 源码分析

3.1 为什么Handler能够切换线程执行?

因为最终的处理是在 handleMessage方法中进行的,所以我们看看 handleMessage方法是怎么被调用起来的。
在这里插入图片描述
ActivityThreadmain()方法调用了Looper.loop()方法,
–> loop()方法里边调用了msg.target.dispatchMessage(msg)方法,
–> 这个msg是我们发送的Message,target是创建的Activity中的Handler对象,
–> loop()方法拿到了我们发送的信息
–> loop()方法判断当前线程是否已经调用了Looper.loop()方法,没有则抛出异常,
–> 这就是我们创建非主线程的Handler为什么要调用Looper.prepare()的原因。
–> 主线程在ActivityThread.main()中调用了prepare()方法,不需要再额外创建Looper。
在这里插入图片描述
看看MessageQueue的部分源码:
在这里插入图片描述
Handler中的sendMessage():
在这里插入图片描述
sendMessage()方法最终是调用了 sendMessageAtTime()方法,这个方法首先将会拿到一个消息队列 mQueue,这个队列是在创建 Looper的时候默认初始化的,然后会调用enqueueMessage()方法进队。
在这里插入图片描述
MessageQueue中的enqueueMessage():
在这里插入图片描述
异步消息与同步消息唯一的区别就是当有消息屏障时,异步消息还可以执行,而同步消息则不行。

总结一下:

Handler发送的线程不处理消息,只有Looper.loop()将消息取出来后再进行处理,所以在Handler机制中,无论发送消息的Handler对象处于什么线程,最终的处理又是运行在Looper.loop()所在的线程。

3.2 Handler.post(Runnable) 方法是运行在新的线程吗?

调用我们post方法里传递的 Runnable 对象的run()方法,Runnable 跟线程没有半毛钱关系,他只是一个回调方法而已。

3.3 Handler(Callback) 跟 Handler() 这两个构造方法的区别在哪?

HandlerdispatchMessage()方法中,msg先判断callback为不为空,再判断mCallback是否为空(我们自己传的这个Callback),为空调用handleMessage(msg)不为空调用mCallback.handleMessage(msg),根据mCallback.handleMessage(msg)的返回值判断是否拦截消息,如果拦截(返回 true),则结束,否则还会调用 Handler#handleMessage(msg)方法。
总结来说:Callback.handleMessage() 的优先级比 Handler.handleMessage()要高 。如果存在Callback,并且Callback#handleMessage() 返回了 true ,那么Handler#handleMessage()将不会调用。

3.4 子线程可以创建 Handler 吗?

  可以。但是有一些注意事项,子线程创建 Handler 除了需要调用 Looper.prepare()外,还需要调用 Looper.loop()启动。
  任何线程都可以创建 Handler,只要当前线程调用了 Looper.prepare()方法,那么就可以使用 Handler 了,而且同一线程内就算创建 n 个 Handler 实例,也只对应一个 Looper,即对应一个消息队列。
  一个线程可以有多个Handler实例,只对应一个Looper,一个MessageQueue。

3.5 为什么主线程不用调用 Looper.prepare() ?

在这里插入图片描述
在App启动的时候系统默认启动了一个主线程的 Looper,prepareMainLooper()也是调用了 prepare()方法,里面会创建一个不可退出的 Looper,并 set 到 sThreadLocal对象当中。

3.6 为什么创建 Message 对象推荐使用 Message.obtain()获取?

因为 Handler 机制在整个 Android 系统中使用太频繁,所以 Android 就采用了一个缓存策略。就是 Message 里面会缓存一个静态的消息池,当消息被处理或者移除的时候就会被回收到消息池,所以推荐使用 Message.obtain()来获取消息对象。

3.7 梳理

在这里插入图片描述
  把整个Handler机制比作一个流水线的话,那么 Handler 就是工人,可以在不同线程传递 Message到传送带(MessageQueue),而传送带是被马达(Looper)运输的,马达又是一开始就运行了(Looper.loop()),并且只会在一开始的线程,所以无论哪个工人(Handler)在哪里(任意线程)传递产品(Message),都只会在一条传送带(MessageQueue)上被唯一的马达(Looper)运送到终点处理,即 Message 只会在调用 Looper.loop() 的线程被处理。

4. 常见问题&技巧

4.1 为什么 Handler 会造成内存泄漏?

生命周期长的对象引用了生命周期短的对象。 Handler 里面匿名内部类的 Handler 持有 Activity 的引用,而发送的 Message 又持有 Handler 的引用,Message 又存在于 MessageQueue 中,而 MessageQueue 又是 Looper 的成员变量,并且 Looper 对象又是存在于静态常量 sThreadLocal 中。sThreadLocal 间接的持有了 Activity 的引用,当 Handler 发送的消息还没有被处理完毕时,比如延时消息,而 Activity 又被用户返回了,即 onDestroy() 后,系统想要对 Activity 对象进行回收,但是发现还有引用链存在,回收不了,就造成了内存泄漏。
在这里插入图片描述

4.2 怎么防止 Handler 内存泄漏?

想要防止 Handler 内存泄漏,一种方法是把 sThreadLocal 到 Activity 的引用链断开就行了。
  最简单的方法就是在onPause()中使用 Handler 的 removeCallbacksAndMessages(null)方法清除所有消息及回调。就可以把引用链断开了。
  Android 源码中这种方式也很常见,不在 onDestroy()里面调用主要是 onDestroy() 方法不能保证每次都能执行到

4.3 Looper.loop() 为什么不会造成应用卡死?

按照一般的想法来说,loop() 方法是一个死循环,那么肯定会占用大量的 cpu 而导致应用卡顿,甚至说 ANR 。主要是通过 Linux 的 epoll 机制实现的。

5. 总结

  1. Handler的回调方法是在 Looper.loop()所调用的线程进行的;
  2. Handler的创建需要先调用Looper.prepare(),然后再手动调用loop()方法开启循环;
  3. App 启动时会在ActivityThread.main()方法中创建主线程的Looper ,并开启循环,所以主线程使用 Handler 不用调用第2点的逻辑
  4. 延时消息并不会阻塞消息队列;
  5. 异步消息不会马上执行,插入队列的方式跟同步消息一样,唯一的区别是当有消息屏障时,异步消息可以继续执行,同步消息则不行
  6. Callback.handleMessage() 的优先级比 Handler.handleMessage()要高
  7. Handler.post(Runnable)传递的 Runnale 对象并不会在新的线程执行;
  8. Message 的创建推荐使用 Message.obtain()来获取,内部采用缓存消息池实现;
  9. 不要在 handleMessage()中对消息进行异步处理;
  10. 可以通过removeCallbacksAndMessages(null)或者静态类加弱引用的方式防止内存泄漏
  11. Looper.loop()不会造成应用卡死,里面使用了 Linux 的 epoll 机制

http://chatgpt.dhexx.cn/article/dr1JAvTx.shtml

相关文章

java中handler机制_自己动手撸一个Handler,让你彻底搞懂Handler机制,揍吻你服不服?...

原标题:自己动手撸一个Handler,让你彻底搞懂Handler机制,揍吻你服不服? 一、关于Handler Handler对于我们Android开发者来说应该是再熟悉不过了,这也是在android中最重要的消息机制,特别是在面试笔试时,Handler机制也是最常问到的话题。今天我们就来动手撸一个自己写的H…

一文搞懂Handler机制

什么是Handler? Handler是进程内部、线程间的一种通信机制。 Handler、Looper、MessageQueen、Message的关系 Message: 消息对象 MessageQueen: 存储消息对象的队列 Looper:负责循环读取MessageQueen中的消息,读到消息之后就把消息交给Handler去处理。 Handler&…

Handler机制(异步消息处理机制)

Android的异步消息处理由四个部分组成:Message、Handler 、MessageQueue和Looper。 先抛出自己的几个问题供自己以后进行回顾复习: 1、为什么需要进行异步消息处理? 大量耗时的操作我们一般是另开一个子线程来进行处理,但是在子线…

Android - 浅谈 Handler 机制

熟悉 Android 开发的小伙伴都知道,不能再非主线程中修改 UI 控件,而且当时老师告诉我们就是在非主线程的代码修改 UI 控件可以用 Handler。以至于后来我也都是这么做的,最近有人问我这个自定义的 Handler 为啥下面都是黄色的,是不…

【Android】Handler机制详解

【Android】Handler机制详解 本文是在 Carson带你学Android 作者的异步通信 专栏中Handler的基础上学习整理的kotlin版本,并且Android源码部分也更新至最新。 1.使用Handler消息传递机制的原因 2.相关概念 概念定义作用备注Main Thread应用程序初次启动时会自动开…

一文搞懂Handler机制原理

前言 Android提供了Handler来满足线程间的通信,开发中不管直接还是间接基本离不开Handler的使用,通常Handler被我们用来做子线程更新UI线程的工具,可以说只要有子线程与主线程通信的地方就会有Handler。 工欲善其事必先利其器,熟…

handler机制原理

一,什么是handler handler是消息机制的一个上层接口 更新UI的操作 耗时完毕后发送消息给主线程更新UI 耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制 handler通过发送和处理message和Runnable对象来关联相对应线程的…

Android Handler消息机制原理最全解读(持续补充中)

本文主要详细去解读Android开发中最常使用的Handler,以及使用过程中遇到的各种各样的疑问。 Handler 在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work thread)中去执行,然后将执行的结果告诉UI线程…

Handler机制

1.为何引入Handler机制 Handler是线程间通讯的机制,Android中,网络访问、文件处理等耗时操作必须放到子线程中去执行,否则将会造成ANR异常。 ANR异常:Application Not Response 应用程序无响应 产生ANR异常的原因:在…

Handle消息机制解析

概述 Handler消息机制(由Handler/Looper/MessageQueue等构成),Android有大量的消息驱动方法来进行交互,就像Android的四大组件(Activity、Service、Broadcast、ContentProvider)的启动过程交互,都离不开Handler的消息机制,所以An…

Handler机制(一)——Handler运行流程分析

1 概述 Handler机制是Android的异步消息处理机制,用于在线程间传递消息,主要涉及到四部分:Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者;Message是消息主体;MessageQueue是消息队列&a…

reshape的作用

reshape就是矩阵的变换就是行和列相乘的数相等就可以相互变换

reshape函数

在opencv中,reshape函数比较有意思,它既可以改变矩阵的通道数,又可以对矩阵元素进行序列化,非常有用的一个函数。 函数原型: C: Mat Mat::reshape(int cn, int rows0) const 参数比较少,但设置的时候却要千…

Reshape的命令应用

import numpy as np tnp.arange(0,64).reshape(8,8) print(t) Reshape 的参考使用: (1条消息) Python的reshape的用法:reshape(1,-1)_冷月无声的博客-CSDN博客_reshape函数pythonhttps://blog.csdn.net/qq_29831163/article/details/90112000Reshape主…

matlab中reshape的用法,reshape2 函数 reshape 的用法

函数 reshape 的用法 请我在MATLAB编程中遇到了一个问题,函数reshape的用法我就是没有弄B reshape(A,m,n) 返回一个m*n的矩阵B, B中元素是按列从A中得到的。如果A中元素个数没有m*n个, 则会引发错误。 B reshape(A,m,n,p,...)和B reshape(…

Numpy之reshape()详解

Numpy中reshape的使用方法为:numpy.reshape(a, newshape, orderC) 参数详解:1.a: type:array_like(伪数组,可以看成是对数组的扩展,但是不影响原始数组。) 需要reshape的array2.newshape:新的数组 新形状应与原形状兼容。如果是整数&#xf…

利用Numpy库的方法reshape()对ndarray对象矩阵的形状进行调整

利用Numpy库的函数reshape()对ndarray对象矩阵的形状进行调整 调整矩阵或图像的形状是一个常用的操作。 在Numpy库中,可使用函数reshape()实现此操作。 其函数原型如下: dst numpy.reshape(a, newshape[, orderC])参数意义如下: a—需要调…

关于reshape

X.reshape(X.shape[0], -1).T和X.reshape(-1,X.shape[0]) 虽然矩阵形式仍然一致但矩阵元素排列完全不同 在降低测试集维度时注意,应使用X.reshape(X.shape[0], -1).T

matlab reshape 用法,函数 reshape 的用法

函数 reshape 的用法别问小编过得好不好不好你也帮助不了好也不是你的功劳。 请小编在MATLAB编程中遇到了一个问题,函数reshape的用法小编就是没有弄B = reshape(A,m,n) 返回一个m*n的矩阵B, B中元素是按列从A中得到的。如果A中元素个数没有m*n个, 则会引发错误。 你知道失望…

【晕头晕脑的Python】Python中Reshape函数解析

Reshape函数解析 Reshape()作用:Reshape()实例说明:一维reshape() 为 二维二维数组 reshape 切片,逆置三维Reshape情况 Reshape()作用: Reshape(),函数的作用就是将数据的按照既定的维度进行整…