1. 可能引起内存泄漏的场景
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/*
* 创建一个handler对象,用来处理消息
*/
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
2. 引起内存泄漏的原因
mHandler对象的创建,相当于在Activity的内部创建了一个匿名内部类。在一个外部类中包含一个普通内部类(非static修饰),包括匿名内部类、局部内部类、普通内部类等情况。当创建一个内部类的对象时,这个内部类的对象会隐式的持有这个外部类对象的引用。即,创建mHandler对象时,这个对象的内部会隐式的持有MainActivity的引用,这就会导致,如果mHandler不销毁,则导致Activity无法被回收,就造成了内存泄漏。
3. 为什么Handler不会被销毁
3.1 先来看看Handler的创建
public class Handler {
/*
* 轮询器,在一个线程中循环遍历MessageQueue,当有消息需要处理时,交给Handler进行处理
*/
final Looper mLooper;
final MessageQueue mQueue;
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//3.1.1
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//3.1.2
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
3.1.1 mLooper = Looper.myLooper();
- Looper是一个轮询器;
- Looper对象中有一个MessageQueue的引用,用来记录当前Looper需要轮询的消息(MessageQueue中有一个Message对象,Message对象是一个链表式的结构,需要处理的消息按照需要处理的时间进行排序);
- 有一个静态的ThreadLocal(本质是一个map),用来存储线程以及对应的looper(每个线程只能有一个looper,通过调用Looper.prepare()方法创建,如果一个线程创建多个looper,会抛出异常);
- 这里得到的mLooper就是到ThreadLocal中去取,当然,mHandler中取出的是主线程对应的Looper。
3.1.2 mQueue = mLooper.mQueue;
- 记录当前线程对应的MessageQueue。
3.2 Handler发送消息的机制
到这里,Handler对象的创建就完成了,没有发现有什么特殊的地方,也没有导致内存泄漏的理由啊。接着往下看,我们之所以创建Handler的对象,无非就是发消息,以及处理消息。
3.2.1 Handler发送消息
public class Handler {
//发送消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
}
发送消息的代码在这里了,请看最后一个方法,enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis);方法体中,msg.target = this; 即msg对象持有了当前Handler的引用。这就意味着,在这个msg处理完之前,将一直持有当前handler对象的引用,而handler对象又持有一个隐式的Activity对象的引用,就导致在msg未销毁之前,Activity无法回收,从而引起内存泄漏。
4. 解决方案
之所以内存泄漏,无非就是因为内部类隐式的持有外部类对象的引用,导致外部类对象无法被回收。这样,我们只需要把非静态的内部类改成静态的,这样,静态的内部类就不会持有外部类对象的引用了,也就不会引起内存泄漏的问题了。还是看代码。
/*
* 处理消息的接口,便于在不同的Activity中实现不同的HandleMessage方法
*/
public interface IHandleMessage {
void handleMessage(Message msg);
}
public class MainActivity extends FragmentActivity implements IHandleMessage {
private MyHandler mHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessage(0);
}
@Override
public void handleMessage(Message msg) {
}
static class MyHandler extends Handler {
private WeakReference<IHandleMessage> mWeakReference;
public MyHandler(IHandleMessage handleMessage) {
mWeakReference = new WeakReference<>(handleMessage);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mWeakReference != null) {
mWeakReference.get().handleMessage(msg);
}
}
}
}
5. 总结
本篇内容仅为个人经验的一些总结,仅供参考,如有错误,望大牛予以指正。
5.1 关于内部类
普通的内部类(非static修饰),创建的内部类对象都会隐式的持有外部类对象的引用。
5.2 关于Looper
一个线程只能有一个Looper与之对应,且每个Looper对应一个MessageQueue。
5.3 关于Handler
Handler可以在子线程中创建 ,只是在创建时,需要调用Looper.prepare()方法先创建Looper。
5.4 关于Message
Message发送消息以后,会将发送消息的Handler引用传递给Msg对象,在处理时,通过当前Msg拿到target对应的Handler对象进行消息的处理,至于处理的线程,当然就是创建Handler的线程。