美文网首页Android开发程序员Android开发
Android中使用Handler为何造成内存泄漏?

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

作者: 881ef7b85f62 | 来源:发表于2019-03-11 20:39 被阅读49次

    目录: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.优化方案

    Activity销毁时及时清理消息队列;
    自定义静态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造成内存泄漏的方法如下:

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

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

    关于handler以及内存问题,涉及到大量的概念和知识点,如果没有系统的学习,很容易会杂糅概念而辨识不清,在面试与实际工作中都会遇到困难。如果你从事Android开发,具备1年以上工作经验,希望深入浅出了解Android handler,flutter等技术要点,渴望实现技术和职业成长上的双重突破,那么以下福利就很适合你:

    福利1 免费直播课程

    《腾讯课堂Android高级开发工程师系列直播》

    适听人群:Android初、中、高级开发工程师

    3.12-3.18 连续7天每晚8点准时直播,持续进行

    3月12日:手写可自动感知组件生命周期的事件分发机制

    3月13日:Android应用最广知识-注解与代理的故事

    3月14日:架构师必备之Android AOP教程

    3月15日:Java虚拟机原理大揭秘

    3月16日:hook源码实现阿里无闪烁换肤

    3月17日:实现安全可靠的Android网络连接

    3月18日:设计模式应该如何运用到Android项目开发中

    福利2 Android开发资料包

    该资料包中主要包括「Java语言进阶与Android相关技术核」、「2)App开发框架知识体系(app亦对象)」、「360° Android app全方位性能调优」、「Android前沿技术」、「NDK 模块开发」等内容,全方位扩充你的知识体系。

    进阶视频
    技术大纲

    想要参与Android进阶免费系列直播课

    以及获取Android开发工程师资料包的同学,

    点击加入:加入

    免费课程,名额有限,先到先得~~

    相关文章

      网友评论

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

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