Android中使用Handler为何造成内存泄漏?

目录: 1.内存泄漏定义 2.Handler造成内存泄漏的原因 3.优化方案

1.内存泄漏定义

首先我们需要了解Java中的常见内存分配,包括静态存储区(方法区)、栈和堆等。

静态存储区:存储的是静态方法和全局的static数据和常量等,该区域的内存在程序编译时已经分配完成,在程序运行的整个过程都存在。 栈区:在执行方法时,方法体内的局部变量(包括基础数据类型和对象的引用等)都在栈上创建,在方法执行结束后,该区域局部变量所持有的内存会自动释放。栈内存分配运算内置于处理器的指令集中,效率高,但该区域的容量有限。 堆区:又称动态分配区,通常是存储new出来的对象的实例,该部分内存在没有引用时会由GC回收。

因此,我们通常说的内存泄漏是指:在堆区不断的创建对象,在该对象已经使用结束,不会再使用该对象时,但是还存在别的对象(生命周期较长)引用,导致该对象无法及时被GC回收,导致堆区可使用的内存越来越少,导致内存泄漏的产生,最终的后果就是OOM。其实Android中的内存泄漏的原因与Java中类似:生命周期较长的对象持有生命周期较短的对象的引用。

2.Handler造成内存泄漏的原因

在Android中的跨线程交互时,尤其是子线程与UI线程交互时,通过Handler在子线程中发送现象,Handler中做更新UI的操作,如下所示:

private Hand = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               case UPDATE_UI:
                   //更新UI操作
                   break;
           }
       }
   };
复制代码

那么,通过Handler为何可以在子线程发送消息,在handleMessage中可以执行更新UI的操作?我们知道通常只有在主线程(通常指UI线程)可以执行更新UI的操作,因此最终还是调用UI线程更新。调用流程分析如下:

2.1 创建Handler对象

查看源码可以看到如下解释,当我们创建Handler对象时,就与该线程和该线程的消息队列相绑定,如果未与当前线程和线程队列绑定就无法正常执行事件的分发处理。我们在主线程创建Handler,因此就与主线程相绑定,Handler对象隐式的持有外部对象的引用,该外部对象通常是指Activity。

When you create a new Handler, it is bound to the thread / * message queue of the thread that is creating it

2.2消息队列处理

子线程执行处理结束后,通过Handler将消息发送处理,但如果此时当前界面已经销毁(Activity销毁),正常情况下,如果Activity不在使用,就可能被GC回收,但由于子线程仍未处理结束,仍持有Handler的引用(否则无法正常发送Handler消息),而该Handler持有外部Activty的引用,导致该Activity无法正常回收,导致内存的泄漏。 该引用的链如下: MessageQueue -> Message -> Handler -> Activity

3.优化方案

  1. Activity销毁时及时清理消息队列;
  2. 自定义静态Handler类+软引用。

3.1 Activity销毁时及时清理消息队列

在Activity销毁时,调用removeCallbacksAndMessages清除Message和Runnable。

 mHandler.removeCallbacksAndMessages(null);
复制代码
/* * Remove any pending posts of callbacks and sent messages whose
     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
     * all callbacks and messages will be removed.
     */
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }
复制代码

3.2 自定义静态Handler类+弱引用

static class MyHandler extends Handler {
        WeakReference<Activity> mWeakReference;
        private MyHandler(WeakReference<Activity> mWeakReference){
            this.mWeakReference = mWeakReference;
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(mWeakReference!=null){
                Activity activity = mWeakReference.get();
                if(activity!=null){
                    //handler消息处理
                }
            }
        }
    }
复制代码

由3.1的分析中可以看出,Handler造成内存泄漏的主要原因是持有当前Activty的强引用,造成Activity无法及时被回收,我们知道GC处理弱引用的机制为当对象销毁时,即使有弱引用存在,也会将其回收。

4.总结

避免使用Handler造成内存泄漏的方法如下:

  1. 在Activity的onDestory()方法中及时清理handler的消息队列;
  2. 自定义静态Handelr类,避免非静态内部类造成内存泄漏;
  3. 使用弱引用,使引用对象可以及时回收。

通过以上三种方式结合使用,可以有效的避免使用Handler不当,造成内存泄漏的情况。

猜你喜欢

转载自juejin.im/post/5c831e2a6fb9a049a97a8381