【Android面试】Handler专题
文章目录一、基础原理与核心概念1. Handler、Message、MessageQueue、Looper 四者关系2. Looper.prepare () 和 loop () 做了什么主线程为什么不用 prepare3. 一个线程能有几个 Looper、MessageQueue4. Handler 发送消息方式及区别5. 消息屏障是什么作用6. 同步消息 vs 异步消息二、源码深度剖析1. Looper.loop () 流程为什么不 ANR2. MessageQueue.enqueueMessage 如何排序3. MessageQueue.next () 阻塞与唤醒4. Handler.dispatchMessage 执行顺序5. Message.obtain() vs new Message()6. Handler 内存泄漏原因7. Looper.quit() 必须调用吗三、实战问题与异常场景1. 子线程直接 new Handler 会怎样2. 子线程创建 Handler 建议3. 移除消息方式与原理4. post (Runnable) 运行在哪个线程5. Handler 处理消息抛异常会怎样6. 跨线程通信原理四、进阶优化与大厂高频1. IdleHandler 是什么2. 简述postDelayed的原理以及postDelayed 为什么不精准3. loop 死循环为什么进程不退出4. Handler 实现线程切换5. 系统哪些地方用 Handler6. 内存泄漏解决方案7. Handler 性能优化8. Android 10 对 Handler 的变更9. Handler vs RxJava vs 协程10. 消息复用池实现11. 简述HandlerThread五、综合设计与原理延伸1. 手写简易 Handler 核心思路2. Handler 实现定时任务3. 跨进程 Handler 能用吗4. Handler 与 UI 刷新、VSYNC5. 用 Handler 排查卡顿一、基础原理与核心概念1. Handler、Message、MessageQueue、Looper 四者关系Looper不断从 MessageQueue 中取消息驱动消息循环一个线程只有一个 Looper。MessageQueue消息队列按时间优先级维护消息链表。Message承载任务内容、回调、标记、延迟时间。Handler负责发送消息到 MessageQueue、处理消息。整体流程Handler sendMessage → Message 入队 MessageQueue → Looper.loop () 不断 next () 取消息 → 分发给 Handler 处理。2. Looper.prepare () 和 loop () 做了什么主线程为什么不用 prepareLooper.prepare()创建 Looper 实例绑定到当前线程ThreadLocal并创建 MessageQueue。一个线程只能调用一次否则抛异常。Looper.loop()开启死循环不断从 MessageQueue.next () 取消息有消息就处理没消息就阻塞休眠。主线程为什么不用手动 prepareActivityThread.main () 中系统已经调用 Looper.prepareMainLooper () 和 Looper.loop ()。3. 一个线程能有几个 Looper、MessageQueue一个线程只能有 1 个 Looper由 ThreadLocal 保证唯一。Looper 内部持有唯一 1 个 MessageQueue。所以1 线程 1 Looper 1 MessageQueue。4. Handler 发送消息方式及区别常用sendMessage(Message)sendMessageDelayed(Message, delay)sendMessageAtTime(Message, uptime)post (Runnable) → 封装成 Message.callbackpostDelayed / postAtTime区别post 系列最终都是 sendMessage。post (Runnable) 会把 Runnable 赋值给 Message.callback。delayed 是相对时间atTime 是绝对开机时间。5. 消息屏障是什么作用消息屏障Barrier是一种特殊消息target 为 null。作用触发后跳过所有同步消息只执行异步消息。典型场景UI 绘制、VSYNC 到来时优先处理绘制消息保证界面流畅。6. 同步消息 vs 异步消息同步消息普通消息遇到屏障会被阻塞。异步消息设置 flag FLAG_ASYNCHRONOUS不受屏障拦截。发送异步消息message.setAsynchronous(true)Handler 创建时传 async trueAPI 28二、源码深度剖析1. Looper.loop () 流程为什么不 ANRLooper.loop 是死循环真正阻塞在 MessageQueue.next ()。next () 调用 nativePollOnce底层基于 Linux epoll 管道实现阻塞没有消息时主线程休眠发送消息时通过 nativeWake 往管道写数据唤醒线程实现等待 - 唤醒机制。2. MessageQueue.enqueueMessage 如何排序消息按 when执行时间 从小到大插入链表。时间更早的插前面时间相同按先后顺序。不是队列是有序单链表。3. MessageQueue.next () 阻塞与唤醒没有消息或未到执行时间调用 nativePollOnce 阻塞释放 CPU。有新消息入队nativeWake 唤醒线程。屏障存在时只遍历异步消息同步消息跳过。4. Handler.dispatchMessage 执行顺序优先级Message.callbackRunnable优先执行否则 Handler 的 handleMessage (msg)最后才是 Callback 接口源码顺序if (msg.callback ! null) → handleCallback (msg)else if (mCallback ! null) → mCallback.handleMessage (msg)else → handleMessage (msg)5. Message.obtain() vs new Message()new Message ()每次新建对象频繁创建会 GC。obtain ()从全局消息池sPool 复用对象避免频繁 GC。池大小默认 50复用后 recycle 放回池。6. Handler 内存泄漏原因内部类持有外部类引用非静态内部类 / 匿名内部类 Handler会隐式持有外部类如 Activity的引用。Message 持有 Handler 引用发送到 MessageQueue 的消息 msg.target handler只要消息还没被处理Handler 就不会被回收生命周期不一致Activity 销毁了但消息还在队列里延迟消息、排队消息典型泄漏场景非静态内部 Handler匿名内部 HandlerpostDelayed 长时间延迟消息内部类 Runnable 同样会持有外部引用7. Looper.quit() 必须调用吗主线程 Looper 绝对不能 quit子线程手动创建的 Looper业务结束后建议 quit不是强制但不调用会内存泄漏三、实战问题与异常场景1. 子线程直接 new Handler 会怎样会崩溃Can’t create handler inside thread that has not called Looper.prepare()原因子线程默认没有 Looper无法创建 Handler。解决Looper.prepare ()Handler handler new Handler ()Looper.loop ()2. 子线程创建 Handler 建议不建议直接在子线程随便创建并长期持有。规范用法使用 HandlerThread自带 Looper用完调用 quit/quitSafely静态内部类 弱引用3. 移除消息方式与原理removeMessages(int what)removeCallbacks(Runnable)removeCallbacksAndMessages(null)原理遍历 MessageQueue 链表把 target 是当前 Handler、匹配 what 或 callback 的消息从链表摘除。4. post (Runnable) 运行在哪个线程运行在 Looper 所在线程。主线程 Handler → 主线程执行。子线程 Handler → 子线程执行。5. Handler 处理消息抛异常会怎样当前消息崩溃会被 catch 住。Looper 不会退出继续取下一条消息。主线程不会因此崩溃只是该消息任务失败。6. 跨线程通信原理核心Looper 与线程绑定Handler 发消息到对应线程的队列。A 线程 Handler 发送 → B 线程 Looper 取出 → 在 B 线程执行。典型子线程发消息 → 主线程 Handler 更新 UI。四、进阶优化与大厂高频1. IdleHandler 是什么IdleHandler 是 MessageQueue 内部接口注册后主线程消息队列空闲的时候回调 queueIdle。返回 true 代表保留、下次空闲继续执行false 执行一次就移除。常用于首帧渲染完成后执行非紧急轻量任务缺点是时机不确定如果消息队列一直繁忙可能永远不会执行并且代码依旧运行在主线程。2. 简述postDelayed的原理以及postDelayed 为什么不精准postDelayed 把 Runnable 包装成 Message计算when开机时间延迟插入 MessageQueue 有序链表。Looper 循环调用 next ()对比当前时间和队头消息 when没到时间就调用 nativePollOnce 进入 epoll 休眠到点自动唤醒。唤醒后取出消息在 Handler 绑定线程执行 Runnable。消息串行执行前面任务耗时会造成延迟。延迟基于 系统开机时间 uptimeMillis。消息队列可能被前面消息阻塞。系统休眠、CPU 调度会影响执行时机。所以只能保证 “至少延迟”不保证精准。3. loop 死循环为什么进程不退出主线程 loop 是进程生命线退出 loop 则 APP 进程结束。无消息时阻塞在 native不占 CPU。系统事件、触摸、广播、服务等都会唤醒消息循环。4. Handler 实现线程切换原理Handler 绑定目标线程 Looper → 发送消息 → 目标线程 Looper 调度执行。案例子线程网络请求 → 主线程 Handler 更新 UI。5. 系统哪些地方用 HandlerActivity 生命周期调度View 绘制、触摸事件分发BroadcastReceiver 回调Service 启动、绑定AsyncTask旧版本Choreographer 刷新 UI6. 内存泄漏解决方案静态内部类 WeakReferenceActivity 销毁时 removeCallbacksAndMessages (null)避免使用非静态内部类、匿名内部类使用 Lifecycle 自动解绑子线程 Handler 用完 quit7. Handler 性能优化避免大量频繁发送消息使用 Message.obtain () 复用避免在 handleMessage 做耗时操作延迟消息及时移除避免消息堆积IdleHandler 做低优先级任务8. Android 10 对 Handler 的变更加强异步消息限制部分系统 API 限制使用异步消息对后台队列、隐式消息做更严格管控修复部分同步屏障、消息调度漏洞更严格的线程检查与异常提示9. Handler vs RxJava vs 协程Handler轻量、系统原生、适合简单线程切换RxJava流式操作、线程切换强、组合事件强、学习成本高协程轻量线程、结构化并发、代码更简洁、无回调地狱10. 消息复用池实现sPool 是静态全局 Message 链表obtain () 从头部取recycle () 清空字段并放回头部限制最大数量避免内存占用过高优势减少 GC提升流畅度。11. 简述HandlerThreadHandlerThread自带 Looper 的 Thread。普通 Thread 跑完 run 就结束HandlerThread 在 run 里自动执行Looper.prepare()Looper.loop()让子线程拥有消息队列可以用 Handler 发消息给这个子线程串行执行任务。适合需要串行有序执行、带延迟任务、需要可控停止的轻量后台任务典型场景是数据库读写、本地文件操作、设备通信状态机、收敛多线程回调不适合并行任务和长时间阻塞任务。五、综合设计与原理延伸1. 手写简易 Handler 核心思路需要实现ThreadLocal 保存 LooperLooper 持有 MessageQueueMessageQueue 用链表维护消息Handler 发送、入队、调度loop () 循环取消息执行最简模型Looper.prepare () → 创建队列Handler.sendMessage → 入队loop () 轮询 → 分发处理2. Handler 实现定时任务方式sendMessageDelayed 递归发送。优点简单、轻量、同线程安全。缺点不准、队列阻塞会延迟、进程休眠不准。对比 WorkManager/AlarmManager后者更适合后台精确定时。3. 跨进程 Handler 能用吗不能直接用。Handler 基于同一进程内存空间。跨进程需要 Binder 机制。替代AIDL、Messenger、ContentProvider、Broadcast。4. Handler 与 UI 刷新、VSYNCChoreographer 依赖 Handler 接收 VSYNC 信号。绘制消息通过异步消息 屏障优先执行。保证 UI 刷新消息优先级高于普通业务消息。5. 用 Handler 排查卡顿核心原理主线程所有任务都通过 Handler 分发只要在 Looper.loop () 分发消息前后埋点统计执行时间超过阈值就是卡顿。为什么 Handler 能查卡顿主线程的 一切操作UI 绘制、点击事件、生命周期、业务代码都是通过 Handler - Message 执行的所有任务最终都会走到msg.target.dispatchMessage(msg)只要监控这个方法的执行耗时就能找到卡顿点具体实现原理利用looper的setMessageLogging方法Looper.getMainLooper().setMessageLogging(newPrinter(){privatelongstartTime;Overridepublicvoidprintln(Stringline){if(line.contains()){startTimeSystem.currentTimeMillis();}elseif(line.contains()){longdurationSystem.currentTimeMillis()-startTime;if(duration50){// 超过阈值// 打印卡顿堆栈Log.e(卡顿,耗时durationms);Log.e(堆栈,Looper.getMainLooper().getThread().getStackTrace());}}}});

相关新闻

矩阵转置offset计算错误:嵌入式越界bug的完整复盘

矩阵转置offset计算错误:嵌入式越界bug的完整复盘

1. 项目背景与报错场景1.1 一个奇怪的崩溃日志前阵子调一个GD32F407的数据采集项目,两路ADC采样,每轮采128个点,我要把它们排成一个16行8列的矩阵,再转置成8行16列,一边刷LCD,一边按行串口上传。某天下午&a…

2026/10/10 8:41:13 阅读更多 →
Codex-X vs Claude Code:开源仓库级智能体对决,谁能真正“读懂“你的项目

Codex-X vs Claude Code:开源仓库级智能体对决,谁能真正“读懂“你的项目

Codex-X vs Claude Code:开源仓库级智能体对决,谁能真正"读懂"你的项目 【免费下载链接】Codex-X OpenAI Codex 桌面端/CLI 的可视化管理工具,具有Provider/API 切换、会话同步、提示词注入、Skills/MCP 管理、TOML 配置可视化的跨…

2026/10/10 8:41:13 阅读更多 →
告别JSONP与XML测试噩梦:jQuery Mockjax多数据类型Mock完整指南

告别JSONP与XML测试噩梦:jQuery Mockjax多数据类型Mock完整指南

告别JSONP与XML测试噩梦:jQuery Mockjax多数据类型Mock完整指南 【免费下载链接】jquery-mockjax The jQuery Mockjax Plugin provides a simple and extremely flexible interface for mocking or simulating ajax requests and responses 项目地址: https://git…

2026/10/10 8:41:13 阅读更多 →

最新新闻

基于SpringBoot+Vue的知识管理系统设计与全栈实践

基于SpringBoot+Vue的知识管理系统设计与全栈实践

1. 做这个系统的真实出发点:团队知识散落的痛点和方案取舍先说说我为什么想起来做这个东西。公司内部其实一直有文档管理的需求,但现状基本是:技术方案在 Wiki 上一份,项目文档在群文件里一份,个人笔记又散在本地硬盘&…

2026/10/10 9:23:02 阅读更多 →
第 29 章 · 求解线性方程组

第 29 章 · 求解线性方程组

Eigen 最重要的应用之一&#xff1a;解 Ax b。本章讲标准解法、各种求解器怎么选&#xff0c;以及"为什么不要用逆矩阵"。29.1 标准解法&#xff1a;solve() 解 Ax b 的标准写法&#xff1a; Eigen::Matrix2d A; A << 2, 1,1, 3; Eigen::Vector2d b(5, 10);E…

2026/10/10 9:23:02 阅读更多 →
ASCEND:与当地的Gemma一起,将一次小小的散步变成一次真实世界的探险

ASCEND:与当地的Gemma一起,将一次小小的散步变成一次真实世界的探险

这是提交给Hacktoberfest开源人工智能挑战赛:第1周——触摸草地. 附近的公园感觉很普通。给它一扇神秘的门&#xff0c;一个小故事&#xff0c;一个在尽头等待的叶耳伴&#xff0c;同样的散步变成了一场冒险。 这就是背后的想法上升。下一层是你前门外的某个地方。 我建造的…

2026/10/10 9:23:02 阅读更多 →
impeccable不是项目,而是可量化的交付协议

impeccable不是项目,而是可量化的交付协议

项目标题是“impeccable”&#xff0c;这个词本身不是技术项目、工具、教程或实体产品&#xff0c;而是一个英文形容词&#xff0c;意为“无懈可击的”“完美无瑕的”“无可挑剔的”。它常用于评价表现、设计、执行、工艺、服务、代码质量、用户体验甚至演讲台风——但它从不单…

2026/10/10 9:23:02 阅读更多 →
科技成果评价全流程实操指南:从评价逻辑到专家评审的避坑要点

科技成果评价全流程实操指南:从评价逻辑到专家评审的避坑要点

1. 内容整体设计与思路拆解1.1 科技成果评价到底在评什么先聊聊一个我这些年被问得最多的问题&#xff1a;“科技成果评价&#xff0c;不是把材料收上来、组织几个专家开个会、出个鉴定意见就行了吗&#xff1f;”说这话的人&#xff0c;多半是没真正上手操作过评价项目。科技成…

2026/10/10 9:23:02 阅读更多 →
遥感滑坡图像识别数据集 | 滑坡识别 遥感影像 语义分割 灾害监测 无人机航拍 目标检测 深度学习数据集 计算机视觉9172期

遥感滑坡图像识别数据集 | 滑坡识别 遥感影像 语义分割 灾害监测 无人机航拍 目标检测 深度学习数据集 计算机视觉9172期

遥感滑坡图像识别数据集 | 滑坡识别 遥感影像 语义分割 灾害监测 无人机航拍 目标检测 深度学习数据集 计算机视觉9172期 数据集概述 本数据集整合了来自九个典型滑坡多发地区的遥感数据&#xff0c;涵盖高分卫星影像与无人机UAV航拍图像&#xff0c;旨在为滑坡识别、清单绘制…

2026/10/10 9:22:01 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起&#xff1a;为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念&#xff0c;很多人会觉得它离自己很远——不就是天上的星星怎么转吗&#xff1f;但如果你正在做航天任务规划、遥感数据接收、星座设计&#xff0c;甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起&#xff1a;为什么你的代码里到处都是重复逻辑刚入行那会儿&#xff0c;我写过一个用户管理模块&#xff0c;注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么&#xff0c;能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介&#xff1a;这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目&#xff0c;以Boss直聘岗位数据为对象&#xff0c;适合用作毕业设计、课程设计或期末大作业。资源包共38个文件&#xff0c;约246KB&#xff0c;以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →