零零碎碎的东西总是记不长久,仅仅学习别人的文章也只是他人咀嚼后留下的残渣。无意中发现了这个每日一道面试题,想了想如果只是简单地去思考,那么不仅会收效甚微,甚至难一点的题目自己可能都懒得去想,坚持不下来。所以不如把每一次的思考、理解以及别人的见解记录下来。不仅加深自己的理解,更要激励自己坚持下去。
handler作用
SDK文档是这么说的。
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
我们一般就是用来更新UI线程的。具体点就是在子线程进行耗时操作,比如获取网络图片,然后需要在主线程更新图片,就需要handler+Message+Loop+MessageQueue来帮忙啦。
但是如果你直接创建一个handler对象,然后重写内部handlerMessage方法,那么AS一定会提醒你会有内存泄漏的可能。
为什么会造成内存泄漏
Android内存泄漏:需要被GC回收的对象因为被其他存活的对象所持有引用,而导致GC不能回收此对象。那么这块内存就会在程序运行期间长期被占据,造成系统内存的浪费,使系统运行缓慢甚至崩溃。
那么handler什么时候会造成内存泄漏呢?
发送延迟消息
众所周知,匿名内部类持有外部类的引用,那么handler对象就会持有activity对象的引用。handler发送message到MessageQueue,message持有handler的引用,而MessageQueue会持有message的引用,而MessageQueue是属于TLS(ThreadLocalStorage)线程,是与Activity不同的生命周期。
所以当Activity的生命周期结束后,而MessageQueue中还存在未处理的消息,那么上面一连串的引用关系就不允许Activity的对象被回收,就造成了内存泄漏。
解决办法
知道了内存泄漏是由引用链造成的,那么解决方法也就是破坏上面的引用链。
首先是引用的类型,有强引用、软引用、弱引用、虚引用,上面的引用链都是强引用。
所以第一种方法,自定义静态内部类,如果想使用外部类的方法,那就通过弱引用的方法引入Activity对象。
public class BaseActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void myHandleMessage(Message msg){}
static class MyHandler extends Handler{
WeakReference<BaseActivity> mActivityReference;
public MyHandler(BaseActivity activity){
mActivityReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
BaseActivity activity = mActivityReference.get();
if(activity != null){
activity.myHandleMessage(msg);
}
}
}
}
你可以自定义在BaseActivity中,在其他Activity中创建Myahndler对象,通过重写myHandleMessage方法进行消息处理。
这种方法就是处理了Activity与Handler之间的引用,这种引用可以再GC时被回收。
第二种,就是处理后面的引用。既然是Activity要被回收时还有未被处理的消息,那么在Activity要被回收时清除消息就可以了。
@Override
protected void onDestroy() {
super.onDestroy();
if(mHandler != null){
mHandler.removeCallbacksAndMessages(null);
}
}