今天在做新模塊測試時發現了一個嚴重的問題,當一個activity開啟一個線程時,如果當前activity調用finish()函數不會關閉當前創建的線程。對于每個新建activity,如果activity中的線程發生內存泄漏。在java中線程時垃圾回收機制的根源,也就是說,在運行系統中DVM虛擬機總會使硬件持有運行狀態的進程的引用,結果導致處于運行狀態的線程將永遠不會回收。因此你必須為你的后臺線程實現銷毀邏輯。
先說下問題出現的場景,我在一個activity中創建一個線程,輪詢去發送請求,正常情況下是沒什么問題的,先看下問題代碼:
@OverridePRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { while (1 == 1) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();}這時看Log日志是正常打印,然后我按返回鍵退出當前應用,發現Log日志還在打印,問題出現了,線程被沒有被回收,而且當你再次返回到應用時,會再在后臺創建一個線程,兩個線程同時在跑。在Java中強制關閉線程是非安全性操作,這時我們要為自己的線程添加判斷條件,相關代碼如下:
private MyThread myThread;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myThread = new MyThread(); myThread.start();}private class MyThread extends Thread { private boolean stop = false; @Override public void run() { super.run(); while (!stop) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } } public void close() { stop = true; }}@Overrideprotected void onDestroy() { super.onDestroy(); myThread.close();}這樣,當activity銷毀時走destroy函數然后調用Thread的close,讓線程退出輪詢,保證了線程安全回收。還有另外一個思路來讓線程可以及時回收,我們知道context對象與activity是綁定的,我們可以實例application來暫存當前context與當前context進行比較,我們可以優化上面的代碼,具體代碼如下:
自定義application用來暫存context對象:
public class MyApplication extends Application { static Context appContext; @Override public void onCreate() { super.onCreate(); } public static void setContext(Context context) { appContext = context; }}讓線程去做context比較,這樣我們就可以忽略activity的生命周期:private MyThread myThread;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication.setContext(this); myThread = new MyThread(this); myThread.start();}private class MyThread extends Thread { private boolean stop = false; private Context context; public MyThread(Context context) { this.context = context; } @Override public void run() { super.run(); while (context == MyApplication.appContext) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } }}大功告成。
新聞熱點
疑難解答