Perform any final cleanup 【before】 an activity is destroyed.
x
Perform any final cleanup 【before】 an activity is destroyed.
/** * 測試不取消註冊廣播致使內存泄漏的問題 */ public class MemoryLeaksActivity extends Activity { MyBRReceiver myReceiver; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textView = new TextView(this); textView.setText("音量變化\n有線耳機插入或拔下\n"); setContentView(textView); myReceiver = new MyBRReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.media.VOLUME_CHANGED_ACTION");//音量變化 intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);//有線耳機插入或拔下 registerReceiver(myReceiver, intentFilter); } private class MyBRReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("bqt", "【context】" + context.getClass().getSimpleName());//MemoryLeaksActivity String action = intent.getAction(); switch (action) { case Intent.ACTION_HEADSET_PLUG: int state = intent.getIntExtra("state", 0); if (state == 1) textView.append("\n耳機模式"); else if (state == 0) textView.append("\n外放模式"); break; case "android.media.VOLUME_CHANGED_ACTION": AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int musicVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); int ringVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); textView.append("\n當前音量:musicVolume=" + musicVolume + " ringVolume=" + ringVolume); break; } } } @Override protected void onDestroy() { super.onDestroy(); Log.i("bqt", "【onDestroy被回調了,並不代表Activity被回收了,Receiver更是沒有被回收】"); } }
x
/**
* 測試不取消註冊廣播致使內存泄漏的問題
*/
public class MemoryLeaksActivity extends Activity {
MyBRReceiver myReceiver;
TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("音量變化\n有線耳機插入或拔下\n");
setContentView(textView);
myReceiver = new MyBRReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.media.VOLUME_CHANGED_ACTION");//音量變化
intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);//有線耳機插入或拔下
registerReceiver(myReceiver, intentFilter);
}
private class MyBRReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i("bqt", "【context】" + context.getClass().getSimpleName());//MemoryLeaksActivity
String action = intent.getAction();
switch (action) {
case Intent.ACTION_HEADSET_PLUG:
int state = intent.getIntExtra("state", 0);
if (state == 1) textView.append("\n耳機模式");
else if (state == 0) textView.append("\n外放模式");
break;
case "android.media.VOLUME_CHANGED_ACTION":
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int musicVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int ringVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
textView.append("\n當前音量:musicVolume=" + musicVolume + " ringVolume=" + ringVolume);
break;
}
}
}
protected void onDestroy() {
super.onDestroy();
Log.i("bqt", "【onDestroy被回調了,並不代表Activity被回收了,Receiver更是沒有被回收】");
}
}
今天,在Q羣中有網友發出了網上的一個相對經典的問題,問題具體見下圖(白注:就是上面那張圖)。html
原本是無心寫此文的,但羣裏多個網友熱情很差推卻,因而,撰此文予以分析。android
從這個問題的陳述中,咱們發現,提問者明顯對Android中的幾個基本概念在理解上是存在誤區的(或直接稱之爲理解錯誤)。且這種誤區,我發現是較爲普遍的存在於很多Android開發心中的。api
理解誤區主要體如今對如下幾個概念沒有區分清:app
下面咱們來一個個具體分析下。eclipse
onDestory做爲Activity中生命週期中的一個常見的方法,咱們先來看一下官方文檔中的描述。異步
從這個定義中,咱們得出以下幾點細節:ide
在實際操做中,onDestory回調方法的觸發時機(或稱之爲Activity銷燬的觸發時機)主要表如今以下四種狀況:測試
另外,上述的b中的按下手機上的返回鍵,系統源碼中也是調用了finish()方法。優化
區分上述的ab與cd兩種方式能夠經過isFinishing()方法的返回值來判斷。this
爲了行文方便,且從ab與cd的人爲主觀性角度出發,本文將ab情形稱之爲「主動銷燬」,cd情形稱之爲「被動銷燬」。
相較於onDestory做爲的Activity生命週期中的回調方法,「銷燬」一詞在Activity中更多的表示的是Activity所處聲明週期中的一種「狀態」。
處於此種狀態的Activity實例,對於User Interface層來講是再也不可見的(不管是當前界面仍是按返回鍵等各類狀況)。
實踐中,處於「銷燬狀態」的Activity與上述的Activity銷燬的觸發時機具備一致的邏輯關係,這種邏輯關係具體體現爲:
須要注意的是,處於「銷燬狀態」的Activity,嚴格意義上與當前Activity的真實內存佔用是否釋放沒有直接的對應關係。也就是說,Activity的銷燬,並不意味着Activity的內存就已經被回收。
Android是基於Java基礎之上,雖然在內存回收機制等方面作了必定的處理與優化(主要是基於Dalvik/ART),可是基本的GC原理上並沒有差異。主要表如今:
也就是說,即便堆內存中對象已經處於可回收狀態,但只要GC未被觸發,內存依然被佔用。
在此,須要區分下GC的不可回收狀態與可回收狀態的區別,嚴格意義上來講,其並不是對立面,由於針對可回收狀態,還有可能對應的軟引用與弱引用須要加以考慮。
Android中,內存泄露做爲一個基本的概念,經常被說起且實踐中也需儘可能掌握。網上關於內存泄露的文章林林總總。
終究內存泄露的本質,是指當前對象在實際運行中超出了其自己意義上生命週期範圍的,從而致使本該處於內存可回收狀態的但實際上卻一直處於不可回收狀態的內存佔用非正常現象。
內存泄露在出現,經常見於以下兩種狀況(爲行文方便,下述將發生了內存泄露的對象稱之爲M):
內存泄露過多會致使應用內存的不斷上升,達到必定程度會直接致使內存溢出(OOM)。具體解決內存泄露時,主要都是針對上述AB兩種狀況分析排查便可。
對於Android中的廣播機制,能夠先參考文章:《Android總結篇系列:Android廣播機制》
Activity中動態註冊的廣播接收器,通常性寫法都是此Activity中持有建立的廣播接收器的對象引用,並指明廣播接收器對應的接收廣播類型(IntentFilter)。
Activity中調用registerReceiver(mBroadcastReceiver, intentFilter)方法進行廣播接收器的註冊。此時,經過Binder機制向AMS(Activity Manager Service)進行註冊。
AMS會對應的記錄Activity上下文、廣播接收器以及對應的IntentFilter等內容,並造成相似於消息的發佈-訂閱存儲模式與結構。
當對應的廣播發出時,在定義的廣播接收器的onReceive(context, intent)方法回調中,對於Activity中動態註冊的廣播接收器,onReceive方法回調中的context指的是Activity Context!
也就是說,Activity與mBroadcastReceiver此時其實是經過AMS相互持有強引用的。所以,對於Activity中動態註冊的廣播接收器,必定要在對應的聲明週期回調方法中去unregisterReceiver,以斬斷此關聯。
不然,就會出現當前Activity的內存泄露。