一、什么是Handler
Handler是种消息的传递机制,通过发送Message和处理Meesage和Runnable对象,来关联对应线程的Looper和MessageQueue实现线程消息传递。常用于子线程向主线程发送消息更新ui。当然也有其他用处。
二、Handler的使用
1、sendMessage(Message msg)
2、post (Runnable runnable)
上面两个方法最后也是调用了sendMessageAtTime的方法。
通过构造函数,获取线程对应Handler的方法:
1、
Handler mHandler = new Handler();
在主线程线程(只有在主线程中)中使用此方法就可以获取当前线程的handler,默认与当前线程关联。
在子线程从获取主线程关联的hanlder,可以通过
Handler mHandler = new Handler(Looper.getMainLooper());
或者Handler mHanlder = Handler.getMain();(其实内部实现跟上面的方式是一致的)
方法生成handler。
2、在子线程中
Looper.prepare();
Handler mHandler = new Hanlder();
Looper.looper();
需要生成一个与线程,相关联的looper,没有传looper,Handler默认获取当前线程的looper
总结:获取的是handler跟looper关联的线程有很大关系。
三、Handler实现机制
1、Message:Handler传递的消息体,可以通过 Message msg = Message.obtain() 的方式,获取实例,其内部是在重新利用了静态的Message,避免再通过Message的构造函数生成实例,造成内存浪费。
2、MessageQueue : 存放 Message的消息队列,按照先进先出的顺序存放,单链表的数据结构。跟Looper唯一对应,
3、Looper:消息管理器,Looper负责将Message按照先进先出的顺序放进去MessageQuue中,也不断地从MessageQueue中取出Message,执行hanlder的hanlderMessage(Message msg)的方法进行消息传递。一个线程通过prepare()获取一个单例Looper对象,一个线程只能拥有一个Looper实例进行管理,其中是通过ThreadLocal和当前线程做了一一对应关系。
Looper.prepare()
Handler mhandler = new Handler(){
@handlerMessage(Message msg){
}
Looper.loop();
}
如上代码,在线程new Handler的时候需要一个与Looper对象对消息进行管理,线程默认不会生成一个Looper对象,而主线程不需要通过Looper.prepare()和Looper.loop(),是因为在app启动的时候,在main函数已经帮我们在主线程用Looper.prepare(),Looper.loop(),的方式生成一个Looper单例与主线程关联,所以不需要关系looper,而在子线程中,默认线程是不会跟looper关联管理message的,所以需要通过Looper.prepare(),生成一个与当前线程关联的looper,而handler与looper关联也是与统一线程,而确认关联关系,或者hanlder传looper对象。
Handler通过sendMessage或者post的方法 发送Message消息,Looper负责将Message按照先进先出的方式将放进去MessageQueue,一个线程维护的Looper单例从MessagQueue中不断需要阻塞的方式取出Message,然后执行Handlder的dispatchMessage()的方法,来执行handlerMessage(),或者回调Runnable的回调方法。
四、内存泄露问题
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
1、原因:
Callback是Hanlder的一个接口,new出来实则是创造了一个匿名类,将以上代码放进acitivty代码当中,则成了一个匿名内部类,而一个匿名内部类持有外部类的实例,可能是activity的实例。当acitivty finish后,而hanlder可能仍然在等待子线程的回调,所以会导致一直持有acitivty的实例。
1、解决办法:
a、程序逻辑的方式,主动干掉持有acitivty实例的message,可通过 removeCallbacksAndMessages()的方法移除仍然未回调的message;如果因为子线程一直在执行不回调引起的持有acitivty的原因,可将将子线主动结束掉。
b、static的形式声明为静态内部类,静态内部类不持有外部类的实例,但是不能直接使用外部类的属性方法等,可以通过弱引用的方式。
c、通过外部类的形式。Callback 在外部实现,handleMessage 再回调 给 activity的方法去执行。
另外也有可能是延迟消息,一直存在MessageQueue中,looper就会一直处于阻塞状态,线程也就无法被回收。这时候就需要将MessageQueue中的消息给清除掉,可以调用mHandler.getLooper.quit()或者.quitSafely()(api 18之后);方法清除。