Activitys, Threads, & Memory Leaks



在Android编程中,一个公认的难题是在Activity的生命周期如何协调长期运行的任务和避免有可能出现的内存泄漏问题。考虑下面一段代码,在Activity创建时启动了一个线程,在线程中无限循环。

/**
* Example illustrating how threads persist across configuration
* changes (which cause the underlying Activity instance to be
* destroyed). The Activity context also leaks because the thread
* is instantiated as an anonymous class, which holds an implicit
* reference to the outer Activity instance, therefore preventing
* it from being garbage collected.
*/
publicclass MainActivity extends Activity { @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleOne();
} privatevoid exampleOne() {
newThread() {
@Override
publicvoid run() {
while(true) {
SystemClock.sleep(1000);
}
}
}.start();
}
}
当手机配置发生改变时(例如手机朝向发生改变),会导致整个Activity销毁和重新创建。我们很容易认为Andriod会为我们做好一切清理工作,回收与Activity相关联的内存和正在运行的线程。但是,事情并不和我们想象中的一样!Activity相关联的内存和正在运行的内存都不会回收,会发生泄漏,一个直接的结果就是程序的性能越来越差。

Activity是如何泄漏的


如果读过LZ的前一篇文章会发现一个非静态的匿名类会持有外部类的一个隐式引用(上述代码中的MainActivity),只要非静态的匿名类对象没有被回收,MainActivity就不会被回收,MainActivity所关联的资源和视图都不会被回收,发生比较严重的内存泄漏。要解决MainActivity的内存泄漏问题,只需把非静态的Thread匿名类定义成静态的内部类就行了(静态的内部类不会持有外部类的一个隐式引用),如下代码所示

/**
* This example avoids leaking an Activity context by declaring the
* thread as a private static inner class, but the threads still
* continue to run even across configuration changes. The DVM has a
* reference to all running threads and whether or not these threads
* are garbaged collected has nothing to do with the Activity lifecycle.
* Active threads will continue to run until the kernel destroys your
* application's process.
*/
publicclass MainActivity extends Activity { @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleTwo();
} privatevoid exampleTwo() {
newMyThread().start();
} privatestatic class MyThread extends Thread {
@Override
publicvoid run() {
while(true) {
SystemClock.sleep(1000);
}
}
}
}


现在新创建的Thread不会持有MainActivity的一个隐式引用,当手机朝向发生改变时不会阻止垃圾回收器对旧MainActivity的回收工作~

Thread是如何泄漏的


在提到的第二个问题中,一旦一个新的Activity创建,那么就有一个Thread永远得不到清理回收,发生内存泄漏。Threads在Java中是GC roots;意味着
Dalvik Virtual Machine (DVM) 会为所有活跃的threads在运行时系统中保持一个硬引用,这会导致threads一直处于运行状态,垃圾收集器将永远不可能回收它。出于这个原因,我们应当为threads添加一个结束的逻辑,如下代码所示

/**
* Same as example two, except for this time we have implemented a
* cancellation policy for our thread, ensuring that it is never
* leaked! onDestroy() is usually a good place to close your active
* threads before exiting the Activity.
*/
publicclass MainActivity extends Activity {
privateMyThread mThread; @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleThree();
} privatevoid exampleThree() {
mThread = new MyThread();
mThread.start();
} /**
* Static inner classes don't hold implicit references to their
* enclosing class, so the Activity instance won't be leaked across
* configuration changes.
*/
privatestatic class MyThread extends Thread {
privateboolean mRunning = false; @Override
publicvoid run() {
mRunning = true;
while(mRunning) {
SystemClock.sleep(1000);
}
} publicvoid close() {
mRunning = false;
}
} @Override
protectedvoid onDestroy() {
super.onDestroy();
mThread.close();
}
}


在上述的代码中,当Activity结束销毁时在onDestroy()方法中结束了新创建的线程,保证了thread不会发生泄漏。但是如果你想在手机配置发生改变时保持原有的线程而不重新创建的话,你可以考虑使用fragment来保留原有的线程,以备下一次使用具体做法可以参考我之前的一篇文章
http://blog.csdn.net/tu_bingbing/article/details/9274289,关于这方面
APIdemos中也做了相关的实现。 

结论


在Android中,长时间运行的任务和Acyivity生命周期进行协调会有点困难,如果你不加以小心的话会导致内存泄漏。关于如何处理这个棘手的问题,下面有几个基本的技巧供参考

     
1、使用静态内部类/匿名类,不要使用非静态内部类/匿名类.非静态内部类/匿名类会隐式的持有外部类的引用,外部类就有可能发生泄漏。而静态内部类/匿名类不会隐式的持有外部类引用,外部类会以正常的方式回收,如果你想在静态内部类/匿名类中使用外部类的属性或方法时,可以显示的持有一个弱引用。

     2、不要以为Java永远会帮你清理回收正在运行的threads.在上面的代码中,我们很容易误以为当Activity结束销毁时会帮我们把正在运行的thread也结束回收掉,但事情永远不是这样的!Java threads会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。所以我们应该养成为thread设置退出逻辑条件的习惯。

     3、适当的考虑下是否应该使用线程.Android应用框架设计了许多的类来简化执行后台任务,我们可以使用与Activity生命周期相关联的Loaders来执行简短的后台查询任务。如果一个线程不依赖与Activity,我们还可以使用Service来执行后台任务,然后用BroadcastReceiver来向Activity报告结果。另外需要注意的是本文讨论的thread同样使用于AsyncTasks,AsyncTask同样也是由线程来实现,只不过使用了Java5.0新增并发包中的功能,但同时需要注意的是根据官方文档所说,AsyncTask适用于执行一些简短的后台任务







Activitys, Threads, & Memory Leaks的更多相关文章

  1. [转]Activitys, Threads, & Memory Leaks

    转自:http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html http://www.cnblo ...

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

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

  3. On Memory Leaks in Java and in Android.

    from:http://chaosinmotion.com/blog/?p=696 Just because it's a garbage collected language doesn't mea ...

  4. Diagnosing out of memory errors and memory leaks 内存泄露实例 C Java JavaScript 内存泄露

    小结: 1. 数据库连接池. JDBC语句和结果对象必须显式地关闭. 2. 电梯到目标楼层后地址是否被释放 When a button is pressed: Get some memory, whi ...

  5. Identify Memory Leaks in Visual CPP Applications —— VLD内存泄漏检测工具

    原文地址:http://www.codeproject.com/Articles/1045847/Identify-Memory-Leaks-in-Visual-CPP-Applications 基于 ...

  6. 解决:Detected memory leaks

    最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks!Dumping objects ->{171} normal block at 0x05785AD0, ...

  7. [Angular2 Router] Exiting an Angular 2 Route - How To Prevent Memory Leaks

    In this tutorial we are going to learn how we can accidentally creating memory leaks in our applicat ...

  8. The Introduction of Java Memory Leaks

    One of the most significant advantages of Java is its memory management. You simply create objects a ...

  9. 【转】简单内存泄漏检测方法 解决 Detected memory leaks! 问题

    我的环境是: XP SP2 . VS2003 最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks! Dumping objects -> {98500} n ...

随机推荐

  1. 类型 - PHP手册笔记

    类型简介 PHP 支持 8 种原始数据类型. 四种标量类型: boolean(布尔型,不区分大小写) integer(整型) float(浮点型,也称作double) string(字符串) 两种复合 ...

  2. Immediate Decodability(字典树)

    Immediate Decodability Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/O ...

  3. JS面向对象编程之:封装、继承、多态

    最近在实习公司写代码,被隔壁的哥们吐槽说,代码写的没有一点艺术.为了让我的代码多点艺术,我就重新温故了<javascript高级程序设计>(其中几章),然后又看了<javascrip ...

  4. XML的命名空间

    XML命名空间提供避免元素命名冲突的方法. 命名冲突:在XML中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突. 这个XML文档携带着某个表格中的信息: <ta ...

  5. VS2010中添加dll目录

    RT,比如用VS写QT,用qmake生成的项目,需要在项目属性里设置:调试->环境,path=%path%;C:\Qt\4.8.5\bin 这样省的每次都要把一堆dll复制到debug/rele ...

  6. 要熟悉QT的所有类和元类系统,当然还有qmake

    http://doc.qt.io/qt-5/classes.html http://doc.qt.io/qt-5/gettingstarted.html http://doc.qt.io/qt-5/q ...

  7. Jmeter性能测试 及压测入门

    Jmeter是一个非常好用的压力测试工具.  Jmeter用来做轻量级的压力测试,非常合适,只需要十几分钟,就能把压力测试需要的脚本写好. 为什么要建立线程组?原因很简单,因为我们要模拟多个线程(用户 ...

  8. logstash 处理多行

    2.2.2 多行事件编码: zjtest7-frontend:/usr/local/logstash-2.3.4/bin# ./plugin list | grep multi Ignoring ff ...

  9. Winform 窗体设计器 无法识别重复成员变量声明的问题

    打开窗体设计视图出现如下错误: 查看后台代码: ColumnHeader colHead; colHead = new ColumnHeader(); colHead.Text = "Ch& ...

  10. linux之SQL语句简明教程---SELECT

    SQL是用来做什么的呢?一个最常用的方式是将资料从数据库中的表格内选出.从这一句回答中,我们马上可以看到两个关键字: 从 (FROM) 数据库中的表格内 选出 (SELECT).(表格是一个数据库内的 ...