只要非靜態(tài)的匿名類對象沒有被回收奏司,MainActivity就不會被回收,MainActivity所關聯(lián)的資源和視圖都不會被回收樟插,發(fā)生比較嚴重的內存泄漏韵洋。
/**
* 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() {
newThread() {//匿名內部類,非靜態(tài)的匿名類會持有外部類的一個隱式引用
@Override
publicvoid run() {
while(true) {
SystemClock.sleep(1000);
}
}
}.start();
}
}
要解決MainActivity的內存泄漏問題黄锤,只需把非靜態(tài)的Thread匿名類定義成靜態(tài)的內部類就行了(靜態(tài)的內部類不會持有外部類的一個隱式引用):
/**
* 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.
*/
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);
}
}
}
}
現(xiàn)在新創(chuàng)建的Thread不會持有MainActivity的一個隱式引用搪缨,當手機屏幕旋轉時不會阻止垃圾回收器對舊MainActivity的回收工作。
Thread是如何泄漏的
在提到的第二個問題中鸵熟,一旦一個新的Activity創(chuàng)建副编,那么就有一個Thread永遠得不到清理回收,發(fā)生內存泄漏流强。Threads在Java中是GC roots;意味著 Dalvik Virtual Machine (DVM) 會為所有活躍的threads在運行時系統(tǒng)中保持一個硬引用痹届,這會導致threads一直處于運行狀態(tài),垃圾收集器將永遠不可能回收它打月。出于這個原因短纵,我們應當為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()方法中結束了新創(chuàng)建的線程香到,保證了thread不會發(fā)生泄漏。但是如果你想在手機配置發(fā)生改變時保持原有的線程而不重新創(chuàng)建的話,你可以考慮使用fragment來保留原有的線程悠就,以備下一次使用具體做法可以參考我之前的一篇文章 http://blog.csdn.net/tu_bingbing/article/details/9274289千绪,關于這方面 APIdemos 中也做了相關的實現(xiàn)。
結論
在Android中梗脾,長時間運行的任務和Acyivity生命周期進行協(xié)調會有點困難荸型,如果你不加以小心的話會導致內存泄漏。關于如何處理這個棘手的問題炸茧,下面有幾個基本的技巧供參考
1瑞妇、使用靜態(tài)內部類/匿名類,不要使用非靜態(tài)內部類/匿名類.非靜態(tài)內部類/匿名類會隱式的持有外部類的引用梭冠,外部類就有可能發(fā)生泄漏辕狰。而靜態(tài)內部類/匿名類不會隱式的持有外部類引用,外部類會以正常的方式回收控漠,如果你想在靜態(tài)內部類/匿名類中使用外部類的屬性或方法時蔓倍,可以顯示的持有一個弱引用。
2盐捷、不要以為Java永遠會幫你清理回收正在運行的threads.在上面的代碼中偶翅,我們很容易誤以為當Activity結束銷毀時會幫我們把正在運行的thread也結束回收掉,但事情永遠不是這樣的碉渡!Java threads會一直存在聚谁,只有當線程運行完成或被殺死掉,線程才會被回收滞诺。所以我們應該養(yǎng)成為thread設置退出邏輯條件的習慣形导。
3、適當?shù)目紤]下是否應該使用線程.Android應用框架設計了許多的類來簡化執(zhí)行后臺任務铭段,我們可以使用與Activity生命周期相關聯(lián)的Loaders來執(zhí)行簡短的后臺查詢任務骤宣。如果一個線程不依賴與Activity秦爆,我們還可以使用Service來執(zhí)行后臺任務序愚,然后用BroadcastReceiver來向Activity報告結果。另外需要注意的是本文討論的thread同樣使用于AsyncTasks等限,AsyncTask同樣也是由線程來實現(xiàn)爸吮,只不過使用了Java5.0新增并發(fā)包中的功能,但同時需要注意的是根據(jù)官方文檔所說望门,AsyncTask適用于執(zhí)行一些簡短的后臺任務