Handler内存泄漏概述及解决

接触到Handler时,加入我们这样写代码:

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

可以发现出现提示This Handler class should be static or leaks might occur

在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类不会持有外部类的引用。

即有可能出现内存泄漏。

何为内存泄漏?

持有一个即将被回收的对象或对象组的引用,但自身未执行完毕,导致持有的引用的对象无法被回收。

首先了解一下GC回收机制:

程序运行起来之后一直在使用内存,而Java通过GC自动检查内存中的对象(检查时机有虚拟机决定),若GC发现对象处于不可达状态(即销毁或不可取,详情参考JAVA对象生命周期),则GC将该对象从内存中回收。也就是说当一个对象变得不被任何引用指向就会在GC发现时被回收。若现在有一组对象,对象A和对象B中只包含互相的引用,却没有任何外部的对象持有对象A或对象B的引用,也就是说这两个对象已经没有意义了,这两个对象(这个对象组)同样属于不可达状态,那么它就会被GC回收。

举个栗子:

在Activity中我们创建一个Handler和一个线程执行网络操作

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case UPDATE_UI_TEXTVIEW:
                //这里执行UI操作,也就是说这里有本Activity的引用
                break;
        }
    }
};

  

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //开个线程执行网络请求
    new Thread(new Runnable() {
        @Override
        public void run() {
            //执行网络请求,完成后向handler发消息让其更新UI
            Message msg = new Message();
            msg.what = UPDATE_UI_TEXTVIEW;
            handler.sendMessage(msg);
        }
    }).start();
}

代码很简单,可以看到我们在onCreate中开启了一个子线程,在其中执行网络请求,并在完成后发消息给handler,handler在handleMessage中进行处理后更新UI。

试想现在我们开启了子线程并执行网络请求,但由于某些原因未等网络请求完成activity便销毁了。此时activity就是一个不可达的对象,那么就可能被GC发现并回收,但是,我们的子线程中网络请求并未执行完成,而巧的是我们的子线程中又含有handler的引用(其实并不巧,大多数子线程都需要更新ui所以都会含有Handler)

handler来自何方?自然来自我们即将被回收的activity

然而activity中的handler对消息处理后会更新activity的ui,自然handler就含有activity的引用,那么内存泄漏就产生了。

内存泄漏的危害是啥?

       内存泄露的危害就是会使虚拟机占用内存过高,导致OOM(内存溢出),程序出错。

  对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC(强制关闭)。

所以我们要尽可能的避免内存泄漏,让我们的程序尽量不发生OMM,FC。

解决方案

我们可以直接在Activity中写一个静态内部类继承自Handler,在其中我们需要得到外部的activity的引用并更新ui,则采用一个弱引用进行处理

private final WeakReference mWeakReference;//创建一个MainActivity的弱引用

MainActivity activity = mWeakReference.get();//在需要用到activity引用时进行获取

那么这个静态内部类就如下:

private static final class MainHandler extends Handler{

    private final WeakReference mainWeakReference;

    private MainHandler(MainActivity mainActivity) {
        mainWeakReference = new WeakReference<>(mainActivity);
    }


    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity mainActivity = mainWeakReference.get();
        if(mainActivity!=null){
            switch (msg.what){
                case 1:
                    break;
            }
        }

    }
}

最后在activity中调用

private MyHandler mMyHandler = new MyHandler(this);//初始化handler

然后就像之前的handler一样在子线程中发消息就可以了!

弱引用为何可以解决内存泄漏?

之前说到了内存泄漏时因为对象B持有对象A引用,对象A即将被回收而对象B却无法结束导致对象A无法被回收。

我们对Activity使用弱引用,弱引用的对象在内存不足时将被GC回收。那么接着上面的流程——activity被销毁即将被回收,子线程中含有handler的引用,handler属于静态内部类,没有activity的引用,也就不会导致内存溢出。我们已经使用弱引用的方式在handler中需要用到activity时得到activity的引用,不用时将被GC回收,也就避免了OMM。

        

 

 

 

 

        

 

 

 

 

 

              

 
 

 

 

 

 


 

 

 

    

 

 

 

 

 

 

 

你可能感兴趣的:(Android)