上一节我们介绍了非静态内部类作为静态变量造成的内存泄漏情况,这一节我们介绍一下Handler的使用造成的内存泄漏情况
知识点
非静态内部类和匿名类内部类的实例都会潜在持有它们所属的外部类的强引用,但是静态内部类却不会
使用匿名内部类
我们来看一段代码:
public class HandlerAct extends AppCompatActivity {
private TextView textView;
private Handler handler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_handler);
textView = (TextView) findViewById(R.id.tv_value);
handler.postDelayed(new Runnable() {
@Override
public void run() {
textView.setText("Finished");
}
}, 5000000L);
}
}
这个Activity中new Runnable()是一个匿名内部类,这个内部类持有外部类Activity的强引用,内部类被封装成消息Message被传递到Handler的消息队列MessageQueue中,即消息持有Activity的强引用。在Message消息没有被Handler处理之前,Activity实例不会被销毁了,于是导致内存泄漏。发送postDelayed这样的消息,你输入延迟多少秒,它就会泄露至少多少秒。而发送没有延迟的消息的话,当队列中的消息过多时,也会照成一个临时的泄露。可参考Android内存泄漏之匿名内部类
使用静态内部类
public class HandlerAct extends AppCompatActivity {
private TextView textView;
private Handler handler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_handler);
textView = (TextView) findViewById(R.id.tv_value);
handler.postDelayed(new MyRunnable(textView), 5000000L);
}
private static final class MyRunnable implements Runnable {
private final TextView mTextView;
protected MyRunnable(TextView textView) {
mTextView = textView;
}
@Override
public void run() {
mTextView.setText("Finished");
}
}
}
这段代码中,我们使用的是静态内部类,那么这样还会内存泄漏吗?
答案:会
上面知识点中我们提到了静态内部类不会持有外部类的引用,那么为什么这里还会内存泄漏呢。
因为TextView持有Activity的强引用,我们都知道View都持有Context的引用,这里的Context就是Activity。new MyRunnable(textView)持有TextView的强引用,这样MyRunnable也就持有Activity的强引用了,所以消息为处理之前,Activity实例不会被销毁,于是导致内存泄漏。
解决方案1:弱引用+静态内部类
匿名内部类因为持有Activity的强引用,所以会导致内存泄漏。
静态内部类中的TextView持有Activity的强引用,所以也会导致内存泄漏
在Android内存泄漏和引用的关系中,我们有讲到弱引用:如果一个对象具有弱引用,在GC线程扫描内存区域的过程中,不管当前内存空间足够与否,都会回收内存。
代码如下:
public class HandlerAct extends AppCompatActivity {
private TextView textView;
private Handler handler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_handler);
textView = (TextView) findViewById(R.id.tv_value);
handler.postDelayed(new MyRunnable(textView), 5000000L);
}
private static final class MyRunnable implements Runnable {
private final WeakReference<TextView> wr;
protected MyRunnable(TextView textView) {
wr = new WeakReference<TextView>(textView);
}
@Override
public void run() {
final TextView tv = wr.get();
if (tv != null) {
tv.setText("Finished");
}
}
}
}
这里我们把静态内部类的TextView改成弱引用了,这样虽然textView持有activity的强引用,但是new MyRunnable(textView)持有的是TextView的弱引用,这样MyRunnable持有Activity的引用也是弱引用,所以内存回收的时候,是可以回收的,我们可以通过wr.get()是否为空判断是否已经回收。
解决方案2:在onDestory的时候,手动清除Message
public class HandlerAct extends AppCompatActivity {
private TextView textView;
private Handler handler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_handler);
textView = (TextView) findViewById(R.id.tv_value);
handler.postDelayed(new Runnable() {
@Override
public void run() {
textView.setText("Finished");
}
}, 5000000L);
}
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
如上方法,我们在ondestory的时候,清除所有未处理的Message,就不会有哪个消息持有Activity的强引用了,这样也不会导致内存泄漏。
解决方案3:使用第三方控件WeakHandler
WeakHandler是一个第三方库,我们看看他是怎么使用的:
public class HandlerAct extends AppCompatActivity {
private TextView textView;
private WeakHandler handler = new WeakHandler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_handler);
textView = (TextView) findViewById(R.id.tv_value);
handler.postDelayed(new Runnable() {
@Override
public void run() {
textView.setText("Finished");
}
}, 5000000L);
}
}
它用起来很简单,不需要考虑弱应用的情况,你只需要把以前的Handler替换成WeakHandler就行了。
我们看看WeakHandler的源代码:
private final WeakHandler.ExecHandler mExec;
public final boolean postDelayed(Runnable r, long delayMillis) {
return this.mExec.postDelayed(this.wrapRunnable(r), delayMillis);
}
private WeakHandler.WeakRunnable wrapRunnable(@NonNull Runnable r) {
if(r == null) {
throw new NullPointerException("Runnable can\'t be null");
} else {
WeakHandler.ChainedRef hardRef = new WeakHandler.ChainedRef(this.mLock, r);
this.mRunnables.insertAfter(hardRef);
return hardRef.wrapper;
}
}
static class WeakRunnable implements Runnable {
private final WeakReference<Runnable> mDelegate;
private final WeakReference<WeakHandler.ChainedRef> mReference;
WeakRunnable(WeakReference<Runnable> delegate, WeakReference<WeakHandler.ChainedRef> reference) {
this.mDelegate = delegate;
this.mReference = reference;
}
public void run() {
Runnable delegate = (Runnable)this.mDelegate.get();
WeakHandler.ChainedRef reference = (WeakHandler.ChainedRef)this.mReference.get();
if(reference != null) {
reference.remove();
}
if(delegate != null) {
delegate.run();
}
}
}
private static class ExecHandler extends Handler {
private final WeakReference<Callback> mCallback;
ExecHandler() {
this.mCallback = null;
}
ExecHandler(WeakReference<Callback> callback) {
this.mCallback = callback;
}
ExecHandler(Looper looper) {
super(looper);
this.mCallback = null;
}
ExecHandler(Looper looper, WeakReference<Callback> callback) {
super(looper);
this.mCallback = callback;
}
public void handleMessage(@NonNull Message msg) {
if(this.mCallback != null) {
Callback callback = (Callback)this.mCallback.get();
if(callback != null) {
callback.handleMessage(msg);
}
}
}
}
源代码中可以清楚的看到它将Handler和Runnable做了弱引用封装,而ExecHandler和WeakRunnable也就是封装之后的内部类。