原文链接:How to Leak a Context: Handlers & Inner Classes
泄露场景
在 Android 中我们经常用 Handler 来处理异步消息,把非主线程的操作结果放在主线程中更新 UI。请思考一下下面的代码有没有问题:
|
|
虽然看起来好像没什么问题,但是这段代码可能引起内存泄露。Android Lint 工具会给出以下警告:
In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class
但是什么东西泄漏了,内存泄漏怎么发生的我们还是不清楚。接下来我们慢慢分析泄漏的原因:
当一个 Android 应用启动时,框架会分配到一个 Looper 实例给应用的主线程。这个 Looper 的主要工作就是处理一个接着一个的消息对象。在 Android 中,所有 Android 框架的事件(比如 Activity 的生命周期方法的调用和按钮的点击等)都是放到消息中,然后加入到 Looper 要处理的消息队列中,由 Looper 依次处理。主线程的 Looper 的生命周期和应用的一样长。
当在主线程中初始化一个 Handler 时,它就会关联到 Looper 的消息队列。发送到消息队列的消息本身就持有 Handler 的引用,只有这样 Looper 在处理这个条消息的时候才能调用 Handler#handleMessage(Message) 处理消息。
在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类则不会。
上面的代码还是很难察觉哪里会导致内存泄漏,那么再看看下面这个例子: