天天看点

【译】Activitys, Threads和 内存泄露

android编程中一个共同的困难就是协调activity的生命周期和长时间运行的任务(task),并且要避免可能的内存泄露。思考下面activity的代码,在它启动的时候开启一个线程并循环执行任务。

【译】Activitys, Threads和 内存泄露
【译】Activitys, Threads和 内存泄露

当配置发生变化(如横竖屏切换)时,会导致整个activity被销毁并重新创建,很容易假定android将会为我们清理和回收跟activity相关的内存及它运行中的线程。然而,这并非如此。这两者都会导致内存泄露而且不会被回收, 后果是性能可能显著地下降。

如果你读过我前一篇关于handler和内部类的文章,那么第一种内存泄露应该很容易理解。在java中,非静态匿名类隐式地持有他们的外部类的引用。如果你不小心,保存这个引用可能导致activity在可以被gc回收的时候被保存下来。activity持有一个指向它们整个view继承树和它所持有的所有资源的引用,所以如果你泄露了一个,很多内存都会连带着被泄露。

【译】Activitys, Threads和 内存泄露
                 图1.在10次配置发生变化后,存留在内存中的activity实例

    每一次配置发生变化后,android系统都会创建一个新的activity并让旧的activity可以被回收。然而,隐式持有旧activity引用的线程,阻止他们被回收。所以每次泄露一个新的activity,都会导致所有跟他们关联的资源都没有办法被回收。

    解决方法也很简单,在我们确定了问题的根源,那么只要将线程定义为private static内部类,如下所示:

【译】Activitys, Threads和 内存泄露
【译】Activitys, Threads和 内存泄露

新的线程不会隐式地持有activity的引用,并且activity在配置发生变化后都会变得可以被回收。

第二个问题是每当创建了一个新activity,就会导致一个thread泄露并且不会被回收。在java中,thread是gc root也就是说在系统中的dalvik virtual machine (dvm)保存对所有活动 中线程的强引用,这就导致了这些线程留存下来继续运行并且不会达到可以被回收的条件。因此你必须要考虑怎样停止后台线程。下面是一个例子:

【译】Activitys, Threads和 内存泄露
【译】Activitys, Threads和 内存泄露

在android中处理activity生命周期与长时间运行的任务的关系可能很困难并且可能导致内存泄露。下面有一些值得考虑的通用建议:

    优先使用静态内部类而不是非静态的。非静态内部类的每个实例都会有一个对它外部activity实例的引用。当activity可以被gc回收时,存储在非静态内部类中的外部activity引用可能导致垃圾回收失败。如果你的静态内部类需要宿主activity的引用来执行某些东西,你要将这个引用封装在一个<code>weakreference</code>中,避免意外导致activity泄露。

    不要假定java最后总会为你清理运行中的线程。在上面的例子中,很容易错误地认为用户退出activity后,activity就会被回收,任何跟这个activity关联的线程也都将一并被回收。事实上不是这样的。java线程会继续运行下去,直到他们被显式地关闭或者整个process被android系统杀掉。因此,一定要记得记得为后台线程实现对应的取消策略,并且在activity生命周期事件发生的时候使用合理的措施。

【译】Activitys, Threads和 内存泄露

<a href="http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html" target="_blank">译文链接:activitys, threads, &amp; memory leaks</a>

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载:http://www.cnblogs.com/kissazi2/p/4125356.html

继续阅读