Android消息传递机制Handler完全解析之内存泄漏等问题
Android Handler的使用还是要注意几个地方的,比如方法的调用和界面退出了Handler消息还在导致内存泄漏等情况。
1、在子线程使用Handler前一定要先为子线程创建Looper,并且让子线程工作
Looper创建的方式是直接调用Looper.prepare()方法,让子线程工作的方法是Looper.loop().
过创建Handler对象时如果没有给它指定Looper,那么它默认会使用当前线程的Looper,而线程默认是没有Looper的,所以使用前一定要先创建Looper。
如果在主线程创建子线程工作的Handler,需要給Handler传入子线程对象HandlerThead。
2、在同一个线程里,Looper.prepare()方法不能被调用两次。因为同一个线程里,最多只能有一个Looper对象。
一个线程里面只能有一个Loooper和一个MessageQueue,如果调用多次创建的方法会抛出异常。
部分源码代码如下:
//Looper对外暴露的创建Looper方法
public static void prepare() {
prepare(true);
}
//参数表示是否允许强制退出,一般是允许的
private static void prepare(boolean quitAllowed) {
//sThreadLocal.get()返回的是跟Looper绑定的对象,如果该对象已经创建,不能再调用prepare方法,否则会抛出异常。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//第一次调用prepare,就会创建Looper,并且把Looper跟ThreadLocal绑定
sThreadLocal.set(new Looper(quitAllowed));
}
//这里是私有方法,只能自身调用
//MessageQueue就是这里实例化的
//一些耗时处理都是在Thread中实现的
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建MessageQueue
mThread = Thread.currentThread();
}
3、只有调用了Looper.loop()方法,Handler机制才能正常工作。
Looper负责管理MessageQueue,它的loop()方法负责从MessageQueue里取出消息并交给Handler处理,
所以如果没有调用Looper.loop()方法,消息就不会被取出和处理。
主线程Looper.loop()方法是Activity创建的时候调用了的,不要我们管,子线程是要我们自己调用的。
调用两次loop方法会怎么样?
源码里面没有报异常,但是loop方法里面有取消息的循环,但是如果多次调用,里面会有多个取消息的循环,是同一个MessageQueu里面取消息,所以多个循环也是没用的,最后还是按顺序一个一个取。
4、Looper.loop()方法一定要在调用了Looper.prepare()方法之后调用。
那是因为如果当前线程还没有Looper,是不能调用Looper.loop()方法开启消息轮询的,否则会报错。
部分源码如下:
/**
* Looper的核心方法
*/
public static void loop() {
final Looper me = myLooper();
//要先调用Looper.prepare(),否则会报错,因为Looper没有实例化
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
}
5、不要在主线程调用Looper.prepare()方法。
这是因为在Android系统创建主线程的时候就已经调用了Looper.prepare()方法和Looper.loop()方法,
这也是为什么我们在主线程使用Handler时不需要调用这两个方法的原因。
6、当我们在子线程使用Handler时,如果Handler不再需要发送和处理消息,那么一定要退出子线程的消息轮询。
如果页面退出,也记得把Handler的消息清空,比如在Activity 退出的回调方法onDestroy中对消息进行清除。
Handler.removeCallbacksAndMessages(null);
7、使用Message.obtain()来获取Message消息对象。
避免过多创建对象。
8、如果在Activity界面接收消息,一般使用弱引用,避免消息在Activity销毁后还在收发消息,造成内存泄漏。
/**
* 声明一个静态的Handler内部类,并持有外部类的弱引用
*/
private static class MyHandler extends Handler{
private final WeakReference<HandlerActivity> mActivty;
private MyHandler(HandlerActivity mActivty) {
this.mActivty = new WeakReference<HandlerActivity>(mActivty);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerActivity activity = mActivty.get();
// 判断Activity没被销毁才继续....
if (activity != null){
}
}
}