Android Handler 内存泄露

内存泄露:

java中的垃圾(GC),垃圾回收的机制受java虚拟机的控制。java虚拟机会不定时的执行 System.gc();
一个本该被回收的对象A,被一个生命周期还没有完成的对象B所持有,导致该被回收的对象A无法被回收。
那么由于B对A的持有,导致了内存泄露。

PS:
对象没有在该释放的时候被释放。

android 中使用handler ,用作消息的传递。

1.在持有handler对象的类内部调用。

class A{
    private String temp="hander问题";
    private Handler  handle=new Handler(){
    // handleMessage
    };
}

handler 作为内部类,在进行信息传递的时候,handler对象一直存在。当此时持有对象A的一个activity,或者fragment被用户销毁的时候。
对象A在垃圾回收机制运行的时候,发现Activity还被其他对象所持有(对象A中还有对象正在运行) 。
那此时handler的存在就导致了内存泄露。

class MyActivity extends Activity{
    private A a= new A();

}

那这种内存泄露如何解决?
问题出在activity销毁的时候,跟他有关的都要销毁,class A ,内部handler 都要销毁。

可以在activity中的销毁的时候,给class A一个消息,(调用函数,回调函数,以及其他的消息传递)将handler内部的what移除。

mHandler.removeMessages(WhatConstants.AUDIO.DOWNLOAD_AUDIO_DONE);
mHandler=null;

现将message的标示移除,然后将handler赋值为空。垃圾回收机制再次完成的时候,activity->A->handler 的持有链就会被回收。

实际场景:

在项目遇到这种情况:

class A extends View {
private Handler  handle=new Handler(){
// handleMessage
};
@Override
protected void onDetachedFromWindow() {
   super.onDetachedFromWindow();
   mHandler.removeMessages(WhatConstants.AUDIO.DOWNLOAD_AUDIO_FILED);
   mHandler=null;
}

}

当这样初始化handler的时候,android studio 会提示这样的warning
这里写图片描述

自己翻译:
如果你这样声明一个handler作为内部类,当垃圾回收机制运行的时候,这个handler就会阻止外部调用类(class A)被垃圾回收掉。
如果这个handler被looper或者MessageQueue 在主线程以外的其他现成中使用,就不会出现问题。但是当个handler被looper或者MessageQueue 在主线程使用,你就需要对这个handler的声明进行处理。像下面这样:

1.声明这个handler作为内部类.(可行,不会出现声明周期的问题)

2.外部类,当你声明这个handler的时候,使用WeakReference(弱引用) 来初始化你的handler,在使用成员class A 的成员变量的时候,使用这个WeakReference。

WeakReference: 当声明一个类采用弱引用的方式进行声明,当垃圾回收机制运行的时候,垃圾回收机制会无视 handler对class A 的持有。直接回收class A。
WeakReference:详细解释—》》》http://www.tuicool.com/articles/imyueq

可是按提示的说法是怎么用的呢 ?

private  MyHandler myHandler= new MyHandler(this);
private class MyHandler extends  Handler{
    WeakReference<A> weakReference;

    public MyHandler(A a){
        weakReference=new WeakReference<A>(a);
    }

    @Override
    public void handleMessage(Message msg) {
        A a= WeakReference.get();
        super.handleMessage(msg);
        switch (msg.what){
    Log.d("~~~", a.temp);
        }
    }
}

这种情况下,当activity销毁的时候,由于handler持有class A 的弱引用 ,activity->A->handler中的activity ,class A 都会被回收。
此时 这种情况虽然解决了内存泄露,又会出现另外一种问题。

!!!!如果handler的生命周期还没有完成呢?<~~~~会崩溃的, crash>

因为handler已经为空了,null.

所有还是需要当activity 被销毁的时候,给class A 一个消息,移除handler中正在执行的任务,移除handler中的message.what。并将handler==null;

在继承了view对象的Class A中有一个方法,

onDetachedFromWindow

这个方法的意思是,当view与activity之间解除关系的时候,会调用这个方法。(如果使用view的时候,完成上述的内存泄露问题的时候,这是一个神技)。也就是activity 给class A 一个消息。

直接在这个方法中对handler正在进行的操作进行处理。一切都是那么的优美。

问题也就妥妥的解决了。

关于内存泄露还会有更多的场景。此处只是在开发中遇到一个场景。但是也具有一般性。

项目中内存泄露的检测:这里给大家推荐一个神器,但是具体的用法还是不熟练。

https://github.com/liaohuqiu/leakcanary-demo

中文讲解:

http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

关于内存泄露其他高人的博客

http://www.linuxidc.com/Linux/2013-12/94065.htm

欢迎大家交流~

猜你喜欢

转载自blog.csdn.net/u012782882/article/details/50571265