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 机制

Android面试必问之Handler机制相关推荐

  1. Android面试必问之触摸事件传递机制

    Android面试必问之触摸事件传递机制 一.Activity的构成 二.触摸事件的类型 三.事件传递的三个阶段 Activity对点击事件的分发过程 五.View的事件分发机制 六.点击事件分发的传 ...

  2. Android面试必问框架原理

    Android面试必问框架原理 volatile的实现原理 synchronized的实现原理 join方法实现原理 CAS无锁编程的原理 ReentrantLock的实现原理 AQS的大致实现思路 ...

  3. 今年Android面试必问的这些技术面,面试心得体会

    前言 不清楚你是不是知道,咱们中国有相当大的一部分软件公司,他们的软件开发团队都小的可怜,甚至只有1-3个人,连一个项目小组都算不上,而这样的团队却要承担一个软件公司所有的软件开发任务,在软件上线和开 ...

  4. 划重点!百度、阿里、腾讯大厂Android面试必问知识点系统梳理,啃一半公司随便挑

    前言 大厂面试一直是我们程序员小伙伴茶余饭后所津津乐道的话题.能进一线互联网大厂工作,也是每个程序员生涯的梦想,为的不仅仅是大厂的种种福利.工作环境和高薪,更为的是大厂的工作氛围,能加入到大牛的圈子, ...

  5. Android面试必问的Activity,初阶,中高阶问法,你都掌握了吗?(要求熟读并背诵全文)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXs1wGDx-1605843173409)(https://upload-images.jianshu.io/uplo ...

  6. 阿里、腾讯大厂Android面试必问知识点系统梳理,满满干货指导

    前言 金三银四,又是一年校招季. 经历过,才深知不易.最近,和作为校招面试官的同事聊了聊,问他们是如何去考察一个学生的,我简单归为以下几点: 聪明.反应快,这点自不必说,聪明意味着学习能力.适应力强, ...

  7. 想学IT的必看!今年Android面试必问的这些技术面,架构师必备技能

    第一次观看我文章的朋友,可以关注.点赞.转发一下,每天分享各种干货技术和程序猿趣事 前言 职场的金三银四跳槽季又来了,不同的是今年比往年「冷」一些,形式更加严峻一些,大家多多少少可能都听到或看到一些信 ...

  8. Android面试必问!2021最新中高阶Android面试题总结,理论+实战双管齐下!

    前言 金九银十面试季,相信大家肯定急需一套Android面试宝典,今天小编就给大家准备了我珍藏已久的Android高阶面试宝典,一份超级详细的Android面试必备知识点,供大家学习 ! 想必每一个安 ...

  9. Android面试必问!斗鱼直播Android开发二面被刷,面试心得体会

    最近很多人说,Android越来越不好找工作了,学习NDK开发会不会好点,今天就聊聊这个问题.是否应该选择学NDK? 1.哪些场景下要用到NDK开发? 跨平台的库,如FFmpeg, skip,weex ...

  10. Android面试必问!面试字节跳动Android研发岗,满满干货指导

    一.自我介绍 应该算是起点比较高吧!985大学毕业后面一直在国外读研.之前准备面试微软但是可能经验不够,没有通过.经过朋友介绍我准备回国,积累一些开发经验.于是我面试了国内大厂BATJ,还有一些其他比 ...

最新文章

  1. JavaScript如何获得input元素value值
  2. 学习笔记——Numpy基本操作(二)
  3. django-数据的插入-利用pymysql
  4. maven servlet配置_Servlet入门
  5. 淘宝开源的代码质量检测工具,太强大了!!
  6. 液晶拼接处理器_大屏幕显示系统设备中矩阵与液晶拼接屏的连接方法
  7. HTML的DOCTYPE是什么意思
  8. android虚拟机共享文件夹在哪里打开,【已解决】Nox夜神安卓模拟器中/mnt/shared对应Mac的共享目录在哪里...
  9. 视频存储网站服务器配置,视频存储服务器配置
  10. matplotlib柱状图上方显示数据_Python数据可视化之matplotlib
  11. 即有分期 提前还款手续费就是不在办理的时候告诉你
  12. 右键添加打开方式 windows
  13. Android:简易的单词本(一)
  14. 何为鸿蒙?和安卓的区别到底是什么?别再扯套壳了
  15. PWM波控制LED灯的亮暗
  16. c语言标准库详解(八):数学公式math.h
  17. 调用操作符和函数对象
  18. layui弹出层html,layer弹出层
  19. 数据分析思维之从整体出发分析零售行业——全方位多方面细节分析
  20. Android感应检测Sensor(简单介绍),Android系统面试题

热门文章

  1. 98.android 全国城市区号,座机号区号匹配对应城市
  2. 【计算机软件基础】如何理解鲁棒性Robust?
  3. 在 Vue 项目中引入 tinymce 富文本编辑器
  4. 卓越的社会化营销人的6个习惯
  5. 超简洁刻录软件ONES全面应用
  6. 小程序 · 引入企业微信中的「在小程序中加入群聊」插件
  7. android开发笔记之多媒体—播放音频(音乐)
  8. web 实现在线拍照。。
  9. linux抓不到终端的包,Linux终端捕获
  10. 阅读ArrayList源码的一些记录