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。