美文网首页工作生活
Android中使用Handler为何造成内存泄漏?

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

作者: 晓涵说 | 来源:发表于2019-07-02 07:08 被阅读0次

    目录:
    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。

    if(mHandler!=null){
       mHandler.removeCallbacksAndMessages(null);
        mHandler=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不当,造成内存泄漏的情况。

    欢迎关注晓涵说(CSDN)xukang868(github)账号信息,查看更多文章。

    相关文章

      网友评论

        本文标题:Android中使用Handler为何造成内存泄漏?

        本文链接:https://www.haomeiwen.com/subject/dcjacctx.html