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. VC++学习之一

    对于编程语言,我一直认为它只是一种工具,就像锤子,斧头一样,每种语言都用自己比较适用的地方,用的时候拿来就可以了.这种思想让我对语言没有做过很仔细的学习,虽然频繁使用过C,C++,java,C#,De ...

  2. diff函数(matlab)

    diff函数式用于求导数和差分的.无论是求导数还是差分,其原理是一样的. 这里简单介绍下其用法: 前后相邻元素之差 上下相邻行之差. 与diff(A,1,1)类似. 第三个参数为2时,则变为列差分运算 ...

  3. Matrix, Her, Transcendence

    The Matrix showed us a traditional perspective of computation theory, which is a hierarchical world ...

  4. Debian安装JAVA环境(转载)

    Debian官方没有维护专门的Java软件包,所以不能直接用apt-get工具来安装.在Debian系统中要安装Java,有两种方式,一种是用传统方式:一种是Debian方式. 1. 传统方式 在 s ...

  5. logback.xml配置详解

    先附上本文分析用的例子: <?xml version="1.0" encoding="UTF-8" ?> <configuration> ...

  6. SQL_SERVER日期函数详细用法

    1.一个月第一天的 Select DATEADD(mm, DATEDIFF(mm,0,getdate()), 0) 2.本周的星期一 Select DATEADD(wk, DATEDIFF(wk,0, ...

  7. docker学习笔记(1)

    (1)Docker介绍 关于Docker的介绍,我就不列举出来了.到百度.谷歌搜索.非常多介绍文章.以下我给出官网的介绍:https://www.docker.com/whatisdocker/ (2 ...

  8. Android控件(一)下拉刷新:SwipeRefreshLayout

    须要注意的是SwipeRefreshLayout以下仅仅能够有一个直接子节点. 布局文件例如以下. <FrameLayout xmlns:android="http://schemas ...

  9. 【Android】Intent中使用Extra传递数据

    传值方法一 Intent intent = new Intent(); Bundle bundle = new Bundle(); //该类用作携带数据 bundle.putString(" ...

  10. 《think in python》学习-7

    think in python 7 迭代 对一个变量可以进行多次赋值 是合法的. 例如以下: bruce = 5 print bruce bruce = 7 print bruce 因为有了多重赋值 ...