1. 前言
现在很多项目,可能Handler用的少了。但是如果你去面试,总是避免不了被问Handler原理等等。
下面将汇总介绍Handler的机制,从简单到复杂让大家都能一文就了解。下次被问到时可以有的放矢。
最简单的介绍,Handler就是一个用于处理多线程异步消息的机制。主要用于线程间通信。并不能支持进程间通信。
2. 正文
常见场景为:后台数据或IO线程获取信息,需要更新UI线程进行界面刷新。
而这两者之中的消息通讯,就可以通过Handler进行处理。
整个Handler 使用是由多个模块组合而成,分别分为:
- Message:代表需要传递的消息,每个消息都有自己的标签。
- MessageQueue :消息队列,通过一个单链表的数据结构来维护消息列表。
- Handler:消息入口和出口,负责向MessageQueue中发送消息(
Handler.sendMessage()
),同时也处理相应的消息事件(Handler.handleMessage()
). - Looper:消息分发者,负责不断循环执行
Looper.loop
将MessageQueue中的消息读取出来,分发给接收者进行处理Message。
2.1 Handler 原理
Android 中的Handler 通信是基于Linux系统的管道通信IPC机制来实现的线程通讯。
Looper.loop方法会不断循环处理Message,其中读取最终会通过JNI调用进入Native层,实现管道通讯。
3. 实例
我们在使用Handler中也有很多需要注意的地方。
3.1 Handler 内存泄漏
Handler 允许我们发送延时消息,但是如果在延时期间用户关闭了Activity,那么该Activity就会出现内存泄漏的问题。
主要是因为Message会持有Handler,而在Java中内部类会持有外部类,也就是Activity会被Handler持有。最终导致Activity无法被销毁,造成泄漏。
解决方法:将Handler定义为静态内部类,弱引用Activity。
实例:
代码语言:javascript复制public class DemoActivity extends AppCompatActivity{
private static class MyHandler extends Handler{
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private WeakReference<DemoActivity> weakReference; public MyHandler(DemoActivity activity) { this.weakReference = new WeakReference(activity);
} @Override
public void handleMessage(Message msg) { DemoActivity activity = weakReference.get(); super.handleMessage(msg); if (null != activity) { //执行业务逻辑
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
3.2 Message 复用
获取 Message 大概有如下几种方式:
代码语言:javascript复制Message message1 = myHandler.obtainMessage(); //通过 Handler 实例获取Message message2 = Message.obtain(); //通过 Message 获取Message message3 = new Message(); //直接创建新的 Message 实例
通过查看源码可知,Handler.obtainMessage()
方法也是调用了 Message.obtain()
方法。
为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。
4. 问题汇总
1.Handler 能够更新UI线程。
2.一个线程可以有多个Handler,我们new Handler的时候并不是创建了线程,而是创建了一个接收者和发送者。
3.一个线程仅有一个Looper。
4.主线程创建的Handler 和子线程创建的Handler有什么区别?
ActivityThread中的main已经针对Looper进行了prepar操作,我们只用直接创建Handler就可以了。
而在子线程中创建Handler需要我们调用Looper.prepare()
进行初始化Looper,然后再调用Lppper.loop()
让Looper进行循环运作下去。
5.Handler是如何确保线程安全的?因为Handler发送消息和取消消息都进行了synchronize
修饰。
6.Handelr的消息延时准确么?并不准确,因为线程上的加锁操作,时间并不能完全准确。
7.Looper一直循环处理会不会导致应
用卡死?
在没有消息产生的时候,Looper会被阻塞(block),并进入休眠,一旦有消息添加,那么就会进行循环处理。