Ref: http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html
安卓开发中一个难点就是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.
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleOne();
}
private void exampleOne() {
new Thread() {
@Override
public void run() {
while (true) {
SystemClock.sleep(1000);
}
}
}.start();
}
}
当配置发生变化时,造成整个ACTIVITY被摧毁重造,很容易以为安卓会进行清理然后重新声明新的内存给ACTIVITY和它的线程。但是不是这样的。两者都会造成泄漏,这回导致严重的性能问题。
怎么泄漏一个ACTIVITY
第一个内存泄漏应该很明显,读过以前写的内部类匿名类泄漏的文章就知道,非静态的内部/匿名类会隐含引用外部ACTIVITY,这样这个ACTIVITY就不会被GC。而ACTIVITY对象又引用了整个VIEW结构和所有它的资源,所有一旦泄漏就会造成大量内存泄漏。
10次 orientation 变化后内存状况;
解决方案,用静态内部类线程:
/**
* 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 garbage collected has nothing to do with the Activity lifecycle.
* Active threads will continue to run until the kernel destroys your
* application's process.
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleTwo();
}
private void exampleTwo() {
new MyThread().start();
}
private static class MyThread extends Thread {
@Override
public void run() {
while (true) {
SystemClock.sleep(1000);
}
}
}
}
这样新线程不会持有外部ACTIVITY的引用了,activity就可以顺利被GC。
怎么泄漏一个线程
下边是线程泄漏的问题。所有激活状态的java线程都在Dalvik虚拟机里有强引用,所以不会被GC。因此使用后台线程时一定要注意实施销毁测策略。下边给出一种解决方案:
/**
* 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.
*/
public class MainActivity extends Activity {
private MyThread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleThree();
}
private void 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.
*/
private static class MyThread extends Thread {
private boolean mRunning = false;
@Override
public void run() {
mRunning = true;
while (mRunning) {
SystemClock.sleep(1000);
}
}
public void close() {
mRunning = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mThread.close();
}
}
上边这段代码在onDestroy()方法里保证永远不会异化的发生线程泄漏。
总结给出的建议:
- 静态内部类好过非静态
- JAVA永远不会GC你的运行中的线程。给你的后台线程设计关闭策略。
- 考虑是否用Thread。查询DB可以用LOADER,小任务用ASYNCTASK。