標籤:bsp cte 退出 聲明 答案 task 應該 mes 執行
線程持久化
Java中的Thread有一個特點就是她們都是直接被GC Root所引用,也就是說Dalvik虛擬機器對所有被啟用狀態的線程都是持有強引用,導致GC永遠都無法回收掉這些線程對象,除非線程被手動停止共置為null或者使用者直接kill進程操作。所以當使用線程時,一定要考慮在Activity退出時,及時將線程也停止並釋放掉
記憶體流失1:AsyncTask
void startAsyncTask() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { while(true); } }.execute();}super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);View aicButton = findViewById(R.id.at_button);aicButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAsyncTask(); nextActivity(); }});
使用LeakCanary檢測到的記憶體流失:
為什嗎?
上面代碼在activity中建立了一個匿名類AsyncTask,匿名類和非靜態內部類相同,會持有外部類對象,這裡也就是activity,因此如果你在Activity裡聲明且執行個體化一個匿名的AsyncTask對象,則可能會發生記憶體流失,如果這個線程在Activity銷毀後還一直在後台執行,那這個線程會繼續持有這個Activity的引用從而不會被GC回收,直到線程執行完成。
怎麼解決?
自訂靜態AsyncTask類,並且讓AsyncTask的周期和Activity周期保持一致,也就是在Activity生命週期結束時要將AsyncTask cancel掉。
記憶體流失2:Handler
代碼如下:
MainActivity.java
...void createHandler() { new Handler() { @Override public void handleMessage(Message message) { super.handleMessage(message); } }.postDelayed(new Runnable() { @Override public void run() { while(true); } }, 1000);}...View hButton = findViewById(R.id.h_button);hButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createHandler(); nextActivity(); }});...
為什嗎?
建立的Handler對象為匿名類,匿名類預設持有外部類activity, Handler通過發送Message與主線程互動,Message發出之後是儲存在MessageQueue中的,有些Message也不是馬上就被處理的。這時activity被handler持有
handler被message持有,message被messagequeue持有,message queue被loop持有,主線程的loop是全域存在的,這時就造成activity被臨時性持久化,造成臨時性記憶體流失
怎麼解決?
可以由上面的結論看出,產生泄漏的根源在於匿名類持有Activity的引用,因此可以自訂Handler和Runnable類並聲明成靜態內部類,來解除和Activity的引用。或者在activity 結束時,將發送的Message移除
記憶體流失3:Thread
代碼如下:
MainActivity.java
void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start();}View tButton = findViewById(R.id.t_button);tButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { spawnThread(); nextActivity(); }});
為什嗎?
Java中的Thread有一個特點就是她們都是直接被GC Root所引用,也就是說Dalvik虛擬機器對所有被啟用狀態的線程都是持有強引用,導致GC永遠都無法回收掉這些線程對象,除非線程被手動停止共置為null或者使用者直接kill進程操作。看到這相信你應該也是心中有答案了吧 : 我在每一個MainActivity中都建立了一個線程,此線程會持有MainActivity的引用,即使退出Activity當前線程因為是直接被GC Root引用所以不會被回收掉,導致MainActivity也無法被GC回收
怎麼解決?
當使用線程時,一定要考慮在Activity退出時,及時將線程也停止並釋放掉
記憶體流失4:Timer Tasks
代碼如下:
MainActivity.java
void scheduleTimer() { new Timer().schedule(new TimerTask() { @Override public void run() { while(true); } },1000);}View ttButton = findViewById(R.id.tt_button);ttButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { scheduleTimer(); nextActivity(); }});
為什嗎?
這裡記憶體流失在於Timer和TimerTask沒有進行Cancel,從而導致Timer和TimerTask一直引用外部類Activity。
怎麼解決?
在適當的時機進行Cancel。
Android記憶體最佳化11 記憶體流失常見情況2 線程持久化