用户名
Email
论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
帖子
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
IT评测·应用市场-qidao123.com技术社区
»
论坛
›
软件与程序人生
›
移动端开发
›
Android Handle 机制常见问题深度解析
Android Handle 机制常见问题深度解析
笑看天下无敌手
论坛元老
|
2025-4-6 03:21:38
|
显示全部楼层
|
阅读模式
楼主
主题
1962
|
帖子
1962
|
积分
5886
目次
引言
1. 一个线程可以有几个 Handler?
2. 一个线程可以有几个 Looper?怎样包管?
3. Handler 内存走漏的原因?为什么其他内部类没有这个问题?
4. 为什么主线程可以 new Handler()?子线程中 new Handler 需要做哪些预备?
5. 子线程 Looper 维护的 MessageQueue 在无消息时如那边理?
6. Handler 线程安全性怎样包管?
7. 精确创建 Message 的方式?
8. 为什么 Looper 死循环不会导致应用卡死?
9. Handler、Thread、HandlerThread 三者的区别?
10. 自己写个 MessageQueue,可以实现跨线程吗?
11. 如果在子线程写一个MessageQueue 可以让其他子线程调用来实现跨线程吗
11.1 为什么不能直接跨线程操纵 MessageQueue?
11.2 怎样精确实现跨线程通讯?
结论
相关推荐
引言
在 Android
开发
中,Handler 机制是异步消息处理的重要工具,主要用于线程间通讯。本文将围绕面试中常见的 Handler 相关问题举行深入解析。
1. 一个线程可以有几个 Handler?
一个线程可以拥有
多个
Handler
,但它们都必须依赖于同一个 Looper。
Handler 需要 Looper 举行消息循环,而 Looper 绑定到 ThreadLocal,一个线程通常只有一个 Looper。
不同 Handler 共享同一个 MessageQueue,可以发送和处理消息。
Handler handler1 = new Handler(Looper.getMainLooper());
Handler handler2 = new Handler(Looper.getMainLooper());
复制代码
2. 一个线程可以有几个 Looper?怎样包管?
一个线程只能有
一个
Looper
,否则会抛出异常。
Looper 通过 ThreadLocal 存储在每个线程的本地存储中。
只能调用 Looper.prepare()
一次
,否则会抛出异常。
Looper.prepare(); // 初始化 Looper
Looper.prepare(); // 第二次调用会崩溃
复制代码
3. Handler 内存走漏的原因?为什么其他内部类没有这个问题?
原因
:
Handler
持有外部类的引用
,如果 Handler 被耽误实行,Activity 大概已经销毁,但Handler 仍然持有它,导致内存走漏。
解决方案
:
使用
静态内部类 + 弱引用
解决。
class MyHandler internal constructor(activity: Activity) : Handler() {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
val activity = activityRef.get()
activity?.let {
// 处理消息(确保在 UI 线程操作 Activity)
}
}
}
复制代码
注意事项:
显式指定 Looper:
将 Handler() 改为 Handler(Looper.getMainLooper()),明确绑定主线程的 Looper。这符合 Android 新版本的要求,避免使用已弃用的无参构造。
确保线程安全:
纵然通过 WeakReference 持有 Activity,仍需确保 handleMessage 中的操纵在 UI 线程实行(如更新 UI)。若消息来自子线程,需要通过 activity?.runOnUiThread { ... } 切换线程。
4. 为什么主线程可以 new Handler()?子线程中 new Handler 需要做哪些预备?
主线程可以直接
new Handler()
的原因
:
Android 启动时,ActivityThread.main() 里已经实行Looper.prepareMainLooper()。
主线程默认就有 Looper,以是可以直接 new Handler()。
子线程中
new Handler()
的预备工作
:
必须手动初始化
Looper
,否则 Handler 无法工作。
Thread {
Looper.prepare()
val handler: Handler = Handler(Looper.myLooper()!!)
Looper.loop()
}.start()
复制代码
5. 子线程 Looper 维护的 MessageQueue 在无消息时如那边理?
当 MessageQueue 为空时,Looper.loop() 会
壅闭(idle)状态
,直到新的消息到达。
通过 nativePollOnce() 让线程进入
等待状态
,避免 CPU 资源浪费。
当有新消息时,线程会被唤醒。
6. Handler 线程安全性怎样包管?
多个
Handler
可以向
MessageQueue
发送消息
,但 MessageQueue 通过
同步锁(
synchronized
)
以及
native
方法
确保线程安全。
入队(enqueueMessage)
使用 synchronized 包管消息添加的安全性。
出队(next)
采用
epoll
机制
监听消息,避免 CPU 轮询浪费。
7. 精确创建 Message 的方式?
推荐使用 Message.obtain() 举行复用,淘汰 GC。
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "数据";
handler.sendMessage(msg);
复制代码
8. 为什么 Looper 死循环不会导致应用卡死?
Looper.loop() 内部是
壅闭等待
,而不是无穷循环。
while (true) {
Message msg = queue.next(); // 可能会阻塞
if (msg == null) continue;
msg.target.dispatchMessage(msg);
}
复制代码
queue.next() 为空时,线程进入
等待状态
,不占用 CPU 资源。
只有当新消息到来时,线程才会被唤醒。
9. Handler、Thread、HandlerThread 三者的区别?
名称主要作用是否有 Looper适用场景Handler处理消息和任务依赖 Looper线程间通讯,UI 更新Thread创建新线程实行任务无一次性背景任务HandlerThread具有 Looper 的线程有需要长期运行的背景任务
选择发起
:
短时间任务
:用 Thread + Handler 传递消息。
长期任务
:用 HandlerThread,避免频繁创建和销毁线程,提高性能。
10. 自己写个 MessageQueue,可以实现跨线程吗?
可以
。每个线程如果要有消息循环的话,需要调用Looper.prepare(),然后Looper会关联一个MessageQueue。也就是说
每个线程有一个MessageQueue。
当创建Handle时,把自己写的MessageQueue关联的Looper作为参数传给Handle即可实现跨线程通讯。
每个线程可以有且仅有一个 MessageQueue
:
当线程调用 Looper.prepare() 初始化自己的 Looper 时,会创建一个与该线程绑定的 MessageQueue。若线程没有显式创建 Looper,则默认没有 MessageQueue。
主线程(UI 线程)默认有一个 MessageQueue
:
Android 系统会自动为主线程创建 Looper 和 MessageQueue,用于处理 UI 事件和消息。
其他线程的 MessageQueue 数量由
开发
者控制
:
若
开发
者主动在其他线程中调用 Looper.prepare() 并启动消息循环(比方通过 HandlerThread),则每个这样的线程会有一个独立的 MessageQueue。
示例:
// 主线程:默认有 1 个 MessageQueue
Handler(Looper.getMainLooper()).post {}
// 子线程 1:显式创建 Looper 和 MessageQueue
val thread1 = HandlerThread("Thread1")
thread1.start()
val handler1 = Handler(thread1.looper)
// 子线程 2:另一个 Looper 和 MessageQueue
val thread2 = HandlerThread("Thread2")
thread2.start()
val handler2 = Handler(thread2.looper)
复制代码
此时进程中存在
3 个 MessageQueue
(主线程 + 两个子线程)。
11. 如果在子线程写一个MessageQueue 可以让其他子线程调用来实现跨线程吗
在 Android 中,
不能直接通过让其他子线程操纵另一个子线程的 MessageQueue 来实现跨线程通讯
。这是由于 MessageQueue 是线程私有的资源,设计上并不支持跨线程的直接访问。但可以通过
Handler 机制
安全地实现跨线程通讯。
11.1 为什么不能直接跨线程操纵 MessageQueue?
线程安全性问题
MessageQueue 内部没有同步机制,如果多个线程同时操纵同一个 MessageQueue(比方插入或删除消息),会导致数据竞争和不可预知的行为。
设计束缚
MessageQueue 是 Looper 的内部实现细节,其生命周期与绑定的线程精密相关。直接操纵 MessageQueue 大概破坏 Android 消息循环的固有逻辑。
11.2 怎样精确实现跨线程通讯?
// 目标线程(子线程 B)
val handlerThread = HandlerThread("WorkerThread")
handlerThread.start()
val targetHandler: Handler = object : Handler(handlerThread.looper) {
override fun handleMessage(msg: Message) {
// 在子线程 B 处理消息
Log.d("ThreadB", "Received message: " + msg.what)
}
}
// 发送线程(子线程 A)
Thread {
// 发送消息到子线程 B
targetHandler.sendEmptyMessage(123)
}.start()
复制代码
消息处理流程
发送消息
:
线程 A 通过 handler.sendMessage() 将消息插入线程 B 的 MessageQueue。
唤醒目的线程
:
若线程 B 的 Looper 处于休眠状态(队列为空),nativeWake() 会唤醒它。
消息取出与处理
:
线程 B 的 Looper 从 MessageQueue 取出消息,并调用 handler.handleMessage() 处理。
结论
Handler 依赖 Looper 和 MessageQueue 实现异步消息处理。
Looper 在子线程中使用时需手动初始化,并确保调用 Looper.loop() 进入消息循环。
MessageQueue 采用
同步锁和 native 方法
确保多线程安全。
Looper.loop()
不会导致卡死
,由于 MessageQueue 为空时线程会进入壅闭状态。
相关推荐
Android 彻底把握 Handler 看这里就够了_extends handler-CSDN博客文章浏览阅读1.7k次,点赞16次,收藏19次。Handler 有两个主要用途:1、安排消息和可运行对象在将来的某个时间实行;2、将要在与您自己的线程不同的线程上实行的操纵排入队列。_extends handler
https://shuaici.blog.csdn.net/article/details/120238927Android Framework 启动流程必知必会-CSDN博客文章浏览阅读1.6k次,点赞3次,收藏4次。Android系统启动涉及Zygote进程,它是通过写时拷贝技术实现资源优化,孵化SystemServer和其他应用进程。SystemServer不直接由init启动,由于Zygote能预先加载资源,避免不必要的服务启动。Zygote孵化应用进程可防止因多线程fork导致的死锁问题。Zygote不使用BinderIPC是为了避免多线程问题,只管Zygote并非单线程,在fork前后管理线程状态。
https://shuaici.blog.csdn.net/article/details/129348678
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
笑看天下无敌手
论坛元老
这个人很懒什么都没写!
楼主热帖
mysql精简单机版,免登录,可复制,不 ...
计算机视觉-OpenCV图像金字塔 ...
Java如何使用流式编程的方式访问url呢 ...
【毕业季】-职场10年大咖有话想说 ...
Gitee教程实战(企业级) 包含详细的出 ...
一个工作薄中快速新建多个数据表 ...
使用ansible部署服务到k8s
使用 Kubeadm 部署 Kubernetes(K8S) 安 ...
什么是真正的HTAP?(一)背景篇 ...
【牛客刷题-SQL进阶挑战】NO1.增删改操 ...
标签云
集成商
AI
运维
CIO
存储
服务器
登录参与点评抽奖加入IT实名职场社区
下次自动登录
忘记密码?点此找回!
登陆
新用户注册
用其它账号登录:
关闭
快速回复
返回顶部
返回列表