一、概念
背景:主线程不能进行耗时操作,耗时操作在子线程完成后需要更新ui,ui的更新在多线程读写且没有同步锁限制可能造成ui的错乱,android使用Handler机制实现线程间的通信。
概念:
英文:A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}.
There are two main uses for a Handler: (1) to schedule messages and
runnables to be executed as some point in the future; and (2) to enqueue
an action to be performed on a different thread than your own.
中文:
Handler用来发送、处理与线程关联的消息队列的Message和Runnable对象。
Handler作用:
1.messages或者 runnables的延迟执行,实现任务的定时或者循环执行;
2.发送与处理消息不在一个线程,实现线程间的通信。
二、关键api
Looper:开启消息循环,从MessageQueue读消息,调用Handler调度消息
MessageQueue:实现消息入队列、出队列
Handler:发送消息、调度消息
Message:实现了序列化,可以在不同进程之间传递
引用网上的一张uml图来描述类的关系更清晰
三、原理
Handler向Looper的消息队列中插入Message,而后再由Looper在消息循环里具体处理。因为消息队列本身不具有链表一变动就能马上感知的功能,所以它需要借助管道和epoll机制来监听变动。当外界向消息队列中打入新消息后,就向管道的“写入端”写入简单数据,于是epoll可以立即感知到管道的变动,从何激发从消息队列中摘取消息的动作
四、使用场景
1.任务的定时执行
2.线程的通信,如OKHttp的回调方法的实现。如果回调方法需要在主线程处理数据,那么绑定主线程的Handler可以在回调方法中向MessageQueue发送消息,绑定主线程的Looper在主线程中读取消息、调度消息
3.handlerthread,封装了looper与handler的创建
五、内存泄露
1.匿名类的引用
原因:匿名Handler实例方法Handler.sendMessage执行在耗时的子线程中,在Activity.finnish后,Handler实例持有Activity实例,无法回收Activity的实例造成的内存泄露
PS:非静态的内部类和匿名内部类的实例都会隐式地持有外部类实例,静态的内部类的实例不会持有外部类的实例。
解决方法:
a.定义静态内部类,它的实例不在持有Activity的实例。如果需要访问Activity的成员变量,Handler可以持有外部Activity实例的软引用或者弱引用。当内存不足或系统GC时,Activity实例会会被回收。
b.Activity.finnish时中断耗时线程
2.Handler.sendMessageDelay()
原因:Handler被Message.target持有,Message发送到MessageQueue中,在Delay时间到达前关闭Activity,有一条MessageQueue -> Message -> Handler -> Activity的引用链,导致Activity实例无法被GC
解决方法:调用Handler.removeMessage(int what)移除MessageQueue中等待处理的消息,如果消息已经在Handler处理,则方法无效