android内存泄露问题分析,Android内存泄漏实例分析

昨天被问到一个内存泄漏问题,正好近期在录制性能优化相关的课程,用我讲的方法来分析问题😯

有时候内存泄漏真是措不及防,潜伏的很深,不用工具的话压根感知不到,往往只有在错误日志里才看得到,不过那个时候也已经晚了。

先上个LeakCanary的warning图

9f5c6f206a84

Paste_Image.png

从图上来看,很直观,MainActivity泄漏了,持有的是MyLinkMovementMethod,还是个static变量。

这么看起来很容易解嘛,在MyLinkMovementMethod里把context引用去掉不就好了么,有什么好问的。

然而看了代码,我也有点懵逼,大家也来瞅瞅。

9f5c6f206a84

Paste_Image.png

这个MyLinkMovementMethod类中只有一个成员变量TouchableSpan,并没有持有context呀,我被误导的去翻了它的父类LinkMovementMethod,发现并没有任何成员变量。那这个锅只能是TouchableSpan来背了。

说来也是诡异的很,当我要TouchableSpan类的截图时,对方给我的是这样的:

9f5c6f206a84

Paste_Image.png

wtf, 这span没有持有context呀,为什么会泄漏?这不科学。onClick(View)也没有泄漏的可能。于是我又让对方发TouchableSpan类给我

9f5c6f206a84

Paste_Image.png

好吧,我也不知道问题出在哪了,被对方牵着走了一路,没线索了。后来他一直在纠结widget.getContext().startActivity(intent)要不要换成applicationContext,这又是另外一个故事了。。。不管用什么context,这里都不存在泄漏。

最后没辙,好歹我也撸了相关课程,连个内存泄漏的原因都找不到,我不甘心啊。。

于是我让他把相关代码都发给我。。。看了代码内心都是崩溃的

9f5c6f206a84

Paste_Image.png

内部类。。。内部类。。。非静态内部类能持有外部的引用啊。心塞。

我们再把整个泄漏的过程梳理下。

9f5c6f206a84

Paste_Image.png

需求是textview能局部点击,整体的onClick事件无法满足,所以需要ClickableSpan来标记。

于是自定义一个TagTextView,在TagTextView中有个内部类Span来监听局部点击,但Span是如何知道自己被点击的呢?用MyLinkMovementMethod来处理textview的onTouch事件。解决方案是对的,但是代码写的有点渣。

MyLinkMovementMethod为单例,TouchableSpan一旦被实例化出来,除非手动置为null,否则不会释放,而由于它是TagTextView的内部类,所以持有这个TextView的引用,TextView是被MainActivity创建出来的,最终导致MainActivity无法被回收。

MyLinkMovementMethod->TagTextView.TouchableSpan->TagTextView->MainActivity

现在再来看LeakCanary的warning

9f5c6f206a84

Paste_Image.png

刚好能对应上了吧,有个工具能帮你做分析挺好的,不然review代码都不一定能找出来。

至于怎么解决?方法倒是很简单的:

TagTextView.Span变为static,静态内部类不持有外部类的引用,就像Handler要用static一样。

MyLinkMovementMethod中不能有Span这个成员变量,让Span引用只存在于某个方法的作用域中。

当TagTextView的TouchEvent为UP时,应手动将Span置为null。

这算是一个比较有代表性的内存泄漏了,如果你对内存泄漏的概念与成因还不够熟悉,可以参考Stay录制的性能优化合辑,从java角度去讲内存泄漏你肯定能看懂。

另外有一点需要改进的,tag的点击事件应该传递到外部去,不应该由Span自己处理跳转到另外一个activity中去,这不符合设计原则,如果某个Tag跳转的activity不是固定的怎么办呢?自定义View的职责应该是单一的,只负责接收事件,解析事件,具体的解决事件应该由上层来处理,不然就没法复用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值