Skip to content

2019-03-08: 自定义 Handler 时如何有效地避免内存泄漏问题? #1

Open
@Moosphan

Description

@Moosphan
Owner
No description provided.

Activity

Ssuiyingsen

Ssuiyingsen commented on Mar 8, 2019

@Ssuiyingsen

1.自定义的静态handler
2.可以加一个弱引用
3.还有一个主意的就是当你activity被销毁的时候如果还有消息没有发出去 就remove掉吧
4.removecallbacksandmessages去清除Message和Runnable 加null 写在生命周的ondestroy()就行

ADrunkenLiBai

ADrunkenLiBai commented on Mar 8, 2019

@ADrunkenLiBai

弱引用会不会出现问题

Ssuiyingsen

Ssuiyingsen commented on Mar 8, 2019

@Ssuiyingsen

不加弱引用的时候 如果GC不了 那岂不是GG了

ADrunkenLiBai

ADrunkenLiBai commented on Mar 8, 2019

@ADrunkenLiBai

不加弱引用的时候 如果GC不了 那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

Ssuiyingsen

Ssuiyingsen commented on Mar 8, 2019

@Ssuiyingsen

不加弱引用的时候如果GC不了那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

我竟无言以对 流弊流弊

windflowersnowandmoon

windflowersnowandmoon commented on Mar 8, 2019

@windflowersnowandmoon

不加弱引用的时候 如果GC不了 那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

Your this kind of train of thought is incorrect, upstairs consciousness is how to avoid the happening of the problem, your train of thought is to do not have a problem namely, it is not my business.The train of thought decides the way out, big brother, see the gap?

windflowersnowandmoon

windflowersnowandmoon commented on Mar 8, 2019

@windflowersnowandmoon

不加弱引用的时候如果GC不了那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

我竟无言以对 流弊流弊

不加弱引用的时候如果GC不了那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

我竟无言以对 流弊流弊

God, can we drink together?

Ssuiyingsen

Ssuiyingsen commented on Mar 8, 2019

@Ssuiyingsen

不加弱引用的时候如果GC不了那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

我竟无言以对流弊流弊

不加弱引用的时候如果GC不了那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

我竟无言以对流弊流弊

天哪,我们可以一起喝酒吗?

Sure

ADrunkenLiBai

ADrunkenLiBai commented on Mar 8, 2019

@ADrunkenLiBai

God, can we drink together?

Sure !

added and removed
underwaythe daily question is solving now
on Mar 11, 2019
FeatherHunter

FeatherHunter commented on Mar 12, 2019

@FeatherHunter
Collaborator

使用弱引用是对的,静态内部类也是对的。
问题2:不使用这些方法,Handler直接持有Activity的引用是否一定会导致内存泄漏?

  1. 如果Handler中没什么耗时操作,任务完成也就释放了Activity引用。
  2. 如果Handler中是一个2秒的操作,在Activity退出的2s后,释放了Activity的指针,这种情况属于短时间的内存泄漏?
  3. Handler中是啥死循环的话,就内存泄露了。

个人写的Handler,且没有耗时操作。没必要保护。
公司项目对于代码规范的话,还是加上保护比较好。避免后续有同事,加上了耗时操作,出现问题。

DaveBoy

DaveBoy commented on Mar 20, 2019

@DaveBoy

是的,正如楼上所说,其实正常情况下是不会内存泄漏的,除非handler队列等待太久(什么情况会这样呢?anr?延时太久?)
理解到泄漏的原因(handler持有了activity,然后message持有handler,然后MQ持有message),持有链了解清楚了也就好解决了:handler持有activity可以通过弱引用或者内部静态类等方式解决,removecallbacksandmessages则是清除后面的引用。
其实却是内存大,泄漏点也没啥,只是规范使然,就像过马路最好走斑马线,不然说不定哪天就被撞死了

mosentest

mosentest commented on Apr 8, 2019

@mosentest

对我而言,handler改为弱引用是一改而论(大家只考虑在activity问题,handler引用了activity),解决问题可以传activity弱引用给handler就行,或者在onresume恢复,onpause移除,谁能保证ondestroy能执行??万一没执行呢?如果在service后台用到handler,难道我也弱引用?合理使用handler,要明白为什么泄漏,不是所有场景都能用弱引用,没记错 高版本内存回收策略会先回收弱软引用,,,再说了,内存高就高,,,,,用户区能存活多久。。。

14 remaining items

frinda

frinda commented on Aug 2, 2019

@frinda

内存泄漏发生的主要原因:当前对象需要被销毁时,由其他对象(包含外部对象或者非静态内部类)持有当前对象的引用,导致当前对象释放不了;也可以抽象的说成是生命周期长的对象持有生命周期短的对象的引用

自定义 handler 导致内存泄漏发生的主要原因:当 activity 调用 finish 方法时,由于消息队列中有没被执行完的 message,message 持有 handler 的引用,handler 作为内部类又持有外部 activity 的引用,导致activity无法被释放;

解决办法:

  1. 把 handler 定义为静态内部类,对外部 activity 的引用使用弱引用的方式
  2. 在 activity 里的 onDestroy 回调方法中,调用 handler的removeCallbacksAndMessages(null)方法,清除消息队列中message
  3. 把 handler 单独定义成一个类,不作为非静态内部类存在
Qiang11

Qiang11 commented on Aug 2, 2019

@Qiang11

直接清除队列里面的message不可以吗?

zhaoyujie

zhaoyujie commented on Aug 2, 2019

@zhaoyujie

看了大家写了这么多,还是很有收获的,目前基本都在使用 kotlin 开发,看见如上的回答中,有一个 kotlin 版本,写法比较奇怪,说一下自己的看法:

private var mHandler: Handler? = WithoutLeakHandler(this)

companion object {
        const val CODE_LOAD_DATA: Int   = 101
        private class WithoutLeakHandler( activity: EditAddressActivity) : Handler(){
            private var mActivity: WeakReference<EditAddressActivity> = WeakReference(activity)

            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                val activity = mActivity.get()
                when(msg.what){

                    CODE_LOAD_DATA -> {
                        //data load started

                    }

                }
            }
        }
    }

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mHandler?.sendEmptyMessage(CODE_LOAD_DATA)
    }`

奇怪的地方:居然把一个所谓的静态内部类定义到了 companion object 模块 ???(该模块只用来定义静态字段和静态方法)

kotlin 中应该怎么写呢:

class WeakHandler(activity: Activity) : Handler() {
        private val weakReference = WeakReference(activity)
        override fun handleMessage(msg: Message?) {
            weakReference.get()?.let {
                // doSomeThings
            }
        }
    }

kotlin 的内部类需要用 inner class 关键字申明,不适用 inner 关键字修饰的类,称之为 嵌套类,不持有外部类的引用,请放心使用哈

Merpyzf

Merpyzf commented on Aug 3, 2019

@Merpyzf

不加弱引用的时候 如果GC不了 那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

千里之堤,毁于蚁穴

ZHANGfeng-james

ZHANGfeng-james commented on Aug 5, 2019

@ZHANGfeng-james

Handler 引起的内存泄露相关知识点:

  1. Java 中内部类;
  2. Java 中 强引用、软引用、弱引用、虚引用;
  3. andorid.os.Handler 的使用,以及如何避免内存泄露。
siren4

siren4 commented on Aug 12, 2019

@siren4

1.自定义静态的Handler
2.非静态的Handler,可以加个弱引用(针对回调)
3.在onDestory时,调用removecallbacksandmessages(null)去清除Message和Runnable.

wangxuyang518

wangxuyang518 commented on Aug 23, 2019

@wangxuyang518

Handler内存泄露的根源:
1.内部类持有外部类的引用
2.Handler的message,没有清除
根据这俩点提出的解决方案:
1.将Handler自定义成静态类
2.清除Message

网友提的 弱引用算长知识了

xinyu618

xinyu618 commented on Aug 27, 2019

@xinyu618

1.将Handler自定义成静态类 弱引用Activity实例
2.清除Message

Mr-taotao

Mr-taotao commented on Sep 29, 2019

@Mr-taotao

1、静态内部类
2、弱引用外部类实例
3、及时移除Message

ddong1031

ddong1031 commented on Mar 10, 2020

@ddong1031

通常大家都喜欢在ondestory的时候调用mHandler.removeCallbacksAndMessages(null)

  • 第一问:handler的源码 相关的了和执行流程,以及这个方法作用执行流程(自行Google)
  • 第二问:但是ondestory的时序问题 ondestory是一定会执行的吗
    个人解答:
    正常点击返回键的话onDestroy方法一定会执行。
    如果是后台强杀的话当前仅有一个activity,这时候,强杀,是会执行onDestroy方法的;如果栈里面的第一个没有销毁的activity会执行ondestroy方法,其他的不会执行。
    比如说:从mainactivity跳转到activity-A(或者继续从activity-A再跳转到activity-B),这时候,从后台强杀,只会执行mainactivity的onDestroy方法,activity-A(以及activity-B)的onDestroy方法都不会执行;
  • 第三问:ondestory 延迟执行怎么解决原因
    个人解答:
    执行完上一个 Activity 的 onResume 之后,Activity 的销毁时通过请求 ActivityManagerService 的 activityIdle() 方法进行,实现的是 MessageQueue.IdleHandler,IdleHandler 会等到 MessageQueue 中当前没有可执行的消息时才会执行,也就是说 Activity 会一直等待主线程消息队列中当前消息都处理完毕了才会进行销毁,这也就是 Activity 的销毁不是立即执行的根本原因;通常代码解决都会定义一个基类的activity 如下
@Override
    protected void onPause() {
        super.onPause();
        if (isFinishing()) {
            destroy();
        }
    }

通过标志位 来判断是否是destroy的状态来加快释放的速度,算是优化的一点

yhxxxbl

yhxxxbl commented on Oct 9, 2020

@yhxxxbl

不加弱引用的时候 如果GC不了 那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

你知道什么是ROM什么是RAM吗?

当你说出这句话时,给人的感觉是你不知道啥是RAM 啥是ROM

dalvik.vm.heapsize 指定了堆空间大小。不是12G的ram应用就能用12G的

zoomc

zoomc commented on Aug 20, 2021

@zoomc

不加弱引用的时候 如果GC不了 那岂不是GG了

现在手机都12G RAM,一个小小的handler就能让他崩溃吗

现在手机内存比我电脑的都高

mlinqirong

mlinqirong commented on Dec 24, 2021

@mlinqirong

handler.removeCallbacksAndMessages(null)清空消息队列

tancolo

tancolo commented on Apr 22, 2024

@tancolo

1.自定义的静态handler
2.可以加一个弱引用
3.还有一个主意的就是当你activity被销毁的时候如果还有消息没有发出去 就remove掉吧
4.removecallbacksandmessages去清除Message和Runnable 加null 写在生命周的ondestroy()就行

群里给出的答案以及讨论都很不错,我这里结合Profiler 直观的将Activity泄漏不同情况展示出来。

image

具体的可以参考我这篇文章自定义Handler内存泄漏 (图文版)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @whiskeyfei@mosentest@mlinqirong@ikakaxi@tancolo

        Issue actions

          2019-03-08: 自定义 Handler 时如何有效地避免内存泄漏问题? · Issue #1 · Moosphan/Android-Daily-Interview