Handler源码学习(四) - Handler源码学习
2018-11-28 本文已影响0人
Hoker_
东拉西扯讲了那么多,终于到Handler了。
一句话概括,Handler就是把特定任务发送到指定线程中执行,比如说UI刷新。如果没有这种需求,仅仅是需要线程的话,个人还是建议用线程池,因为代码更好写,而且线程池功能强大。
Handler的构造方法在前面文章里已经看过了,这里就不再继续说明。主要还是看看发送任务的方法,sendMessageAtTime(),其他的发送消息的方法最终都会调用到这个:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
方法非常简单,就是对Handler绑定的消息队列发送消息,需要注意的是:
msg.target = this;
这里会把Handler对象的引用也放到消息队列里,方便消息队列来识别是哪个Handler的消息被消费。
结合前面的几个分析,Handler源码也没啥特别好讲的了,在说下平时使用Handler经常出的内存泄漏问题吧。
要知道,android内存回收,是采用GCRoot判断引用是否可达来决策的,换句话说,以GCRoot为起点,它可以引用到的对象,在GC时都不会被回收,其他的则会被回收,那么GCRoot有哪些呢:
- 运行中的线程
- 静态对象
- 来自native code中的引用
一般的,我们大多数都是在Activity里这么写上handler:
private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行任务
}
};
但是,内部类(包括匿名类)来会隐式地持有一个外部类对象的引用(在这里就是Activity,不然是无法在里Handler来操作Activity中的View的)而且,我们使用Handler绑定的都是主线程,而主线程的生命周期显然是整个APP运行时期。假如使用Handler的postDelayed()方法,那么在设定的delay到达之前,会有一条Looper->MessageQueue -> Message -> Handler -> Activity的链,导致Activity被持有引用而无法被回收。
解决方法也很简单,用静态类+弱引用:
public static class MyHandler extends Handler{
WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity){
mWeakReference=new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg){
final MainActivity activity=mWeakReference.get();
if(activity!=null){
super.handleMessage(msg);
//执行任务
}
}
}
由于是弱应用,这样在Activity不再使用时,可以被GC掉。