一、如何造成内存泄漏:
1、主线程的Looper对象会伴随该应用程序的整个生命周期
2、Java里,非静态内部类和匿名类都会潜在引用它们所属的外部类
发送的延迟空消息(EmptyMessageDelayed)后、消息处理被前,该消息会一直保存在主线程的消息队列里持续时间,在持续时间里,该消息内部持有对handler的引用,由于handler属于非静态内部类,所以又持有对其外部类(即MainActivity实例)的潜在引用,引用关系如下图
这条引用关系会一直保持直到消息得到处理,从而,这阻止了MainActivity被垃圾回收器(GC)回收,同时造成应用程序的内存泄漏,如下图:
二、解决方法
1、使用静态内部类+弱引用
在Java里,非静态内部类和匿名类都会潜在的引用它们所属的外部类。 但是,静态内部类不会。所以,避免内存泄漏的解决方案是:只需要将Handler的子类设置成静态内部类;同时,还可以加上 使用WeakReference弱引用持有Activity实例。
原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
[java] view plain copy print?
public class MainActivity extends AppCompatActivity {
//将Handler改成静态内部类
private static class FHandler extends Handler{
//定义弱引用实例
private WeakReference reference;
//在构造方法中传入需要持有的Activity实例
public MyHandler(Activity activity) {
reference =new WeakReference(activity); }
//通过复写handlerMessage()从而决定如何进行更新UI操作
@Override
public void handleMessage(Message msg) {
//省略代码
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
//主线程创建时便自动创建Looper和对应的MessageQueue,之前执行Loop()进入消息循环
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化Handler的子类
//这里并无指定Looper,即自动绑定当前线程(主线程)的Looper和MessageQueue
private final Handler showhandler = new FHandler();
//启动子线程
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(10000);
}catch (InterruptedException e) {
e.printStackTrace();
showhandler.sendEmptyMessageDelayed(0x1,10000);
}
}
}.start();
}
2、当外部类结束生命周期时清空消息队列
内存泄漏的原因是: 当Activity结束生命周期时,Handler里的Message可能还没处理完,从而导致一系列的引用关系。其实,我们只要在当Activity结束生命周期时清除掉消息队列(MessageQueue)里的所有Message,那么这一系列引用关系就不会存在,就能防止内存泄漏。
解决方案:当Activity结束生命周期时(调用onDestroy()方法),同时清除消息队列里的所有回调消息(调用removeCallbacksAndMessages(null))。
[java] view plain copy print?
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}