14.Android 控制Handler内存泄露 技巧
Handler 警告
In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class
这是在用Handler不加static
都会出现的一个警告。
Handler 内存泄露缘由
一个Android应用启动的后,都会创建一个主UI线程,再为这个主UI线程提供一个Looper对象,这样所有的Android框架的事件(Activity的生命周期方法调用和事件)都是放入消息中,再加入Looper处理的MessageQueue中。也就是说主线程的Looper生命周期是与Android应用一样长的。
每次在
Handler.sendMessage(message)
的时候,实际已经发送的message已经包含了改Handler的引用。因为在只有这样Looper才能调用处理改message的handler,进而走到handler.handleMessage(Message msg)
中,不然,你想,我发一个message,Looper怎么知道message处理过后告诉哪个Handler执行handler.handleMessage(Message msg)
。非静态的内部类和匿名的内部类都会隐式地持有了它的对应外部类的引用。静态的内部类不会持有外部类的引用。
出现泄漏的场景:假如我的Handler里做了很多耗时的工作,在Activity.finish()
后Handler还在忙碌的话,就一直持有该Activity的引用,Activity无法回收,进行导致Activity持有的很多资源都无法回收,就会出现Handler的内存泄漏
解决思路
解决思路基本上是:不用非静态的Handler,继承Handler的时候,可以选择
- 独立出一个Handler类
- 静态Handler
以上,这两种方法。因为静态的内部类不会持有外部类(Activity)的引用,所以不会导致外部类实例的内存泄露。
在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,所以使用时要非静态内部类时要格外注意,如果实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。
Handler 通用模板
private TextView handlerTV;
private static final int HANDLER_SUCCESS = 206;
private static class RefreshHandler extends Handler {
private final WeakReference<RefreshUIActivity> mActivity;
public RefreshHandler(RefreshUIActivity activity) {
mActivity = new WeakReference<>(activity);
}
/**
* Subclasses must implement this to receive messages.
*
* @param msg
*/
@Override
public void handleMessage(Message msg) {
RefreshUIActivity activity = this.mActivity.get();
if (activity != null) {
switch (msg.what) {
case HANDLER_SUCCESS: {
activity.handlerTV.setText("success");
break;
}
}
}
}
}
private final RefreshHandler refreshHandler = new RefreshHandler(RefreshUIActivity.this);
private final Runnable mRunnable = new Runnable() {
@Override
public void run() {
Message message = RefreshUIActivity.this.refreshHandler.obtainMessage();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
message.what = HANDLER_SUCCESS;
refreshHandler.sendMessageDelayed(message, 2000);
}
};
private final Thread mThread = new Thread(mRunnable);