您的位置:首页 > 移动开发

App调试内存泄露之Context篇(下)

2016-04-19 14:44 127 查看
5. AsyncTask对象

我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncTask等系统自带类去做事情,当然无可厚非。

但是AsyncTask确实需要额外注意一下。它的泄露原理和前面Handler,Thread泄露的原理差不多,它的生命周期和Activity不一定一致。

解决方案是:在activity退出的时候,终止AsyncTask中的后台任务。

但是,问题是如何终止?

AsyncTask提供了对应的API:public final boolean cancel (boolean mayInterruptIfRunning)。

它的说明有这么一句话:

cancel是不一定成功的,如果正在运行,它可能会中断后台任务。怎么感觉这话说的这么不靠谱呢?

是的,就是不靠谱。

那么,怎么才能靠谱点呢?我们看看官方的示例:

  官方的例子是很好的,在后台循环中时刻监听cancel状态,防止没有及时退出。

为了提醒大家,google特意在AsyncTask的说明中撂下了一大段英文:

可怜我神州大陆幅员辽阔,地大物博,什么都不缺,就是缺对英语阅读的敏感。

AsyncTask适用于短耗时操作,最多几秒钟。如果你想长时间耗时操作,请使用其他java.util.concurrent包下的API,比如Executor, ThreadPoolExecutor 和 FutureTask.

学好英语,避免踩坑!

6. BroadcastReceiver对象

... has leaked IntentReceiver ... Are you missing a call to unregisterReceiver()?

这个直接说了,种种原因没有调用到unregister()方法。

解决方法很简单,就是确保调用到unregister()方法

顺带说一下,我在工作中碰到一种相反的情况,receiver对象没有registerReceiver()成功(没有调用到),于是unregister的时候提示出错:

有两种解决方案:

方案一:在registerReceiver()后设置一个FLAG,根据FLAG判断是否unregister()。网上搜到的文章几乎都这么写,我以前碰到这种bug,也是一直都这么解。但是不可否认,这种代码看上去确实有点丑陋。

方案二:我后来无意中听到某大牛提醒,在Android源码中看到一种更通用的写法:

  

7. TimerTask对象

TimerTask对象在和Timer的schedule()方法配合使用的时候极容易造成内存泄露。

  泄露的点是,忘记cancel掉Timer和TimerTask实例。cancel的时机同cursor篇说的,在合适的时候cancel。

8. Observer对象。

Observer对象的泄露,也是一种常见、易发现、易解决的泄露类型。

先看一段正常的代码:

  看完示例,我们来看看病例:

靠,谁这么偷懒,把SettingObserver搞个匿名对象传进去,这可如何是好?

所以,有些懒是不能偷的,有些语法糖是不能吃的。

解决方案就是, 在不需要或退出的时候delete这个Observer。

  注意一点,不同的注册方法,不同的反注册方法。

9. Dialog对象

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@438afa60 is not valid; is your activity running?

一般发生于Handler的MESSAGE在排队,Activity已退出,然后Handler才开始处理Dialog相关事情。

关键点就是,怎么判断Activity是退出了,有人说,在onDestroy中设置一个FLAG。我很遗憾的告诉你,这个错误很有可能还会出来。

解决方案是:使用isFinishing()判断Activity是否退出。

  早完早释放!

10. 其它对象

以Listener对象为主,"把自己搭进去了,切记一定要及时把自己放出来"。

11. 小结

结合本文Context篇和前面Cursor篇,我们枚举了大量的泄露实例,大部分根本原因都是相似的。

通过分析这些例子后,我们应该能理解APP层90%的内存泄露情况了。

至于怎么发现和定位内存泄露,这是另外一个有意思的话题,现在只能说,有方法有工具。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: