Android中一個經典理解誤區的剖析

今天,在Q羣中有網友(@廣州-包晴天)發出了網上的一個相對經典的問題,問題具體見下圖。html

 

原本是無心寫此文的,但羣裏多個網友熱情很差推卻,因而,撰此文予以分析。異步

從這個問題的陳述中,咱們發現,提問者明顯對Android中的幾個基本概念在理解上是存在誤區的(或直接稱之爲理解錯誤)。且這種誤區,我發現是較爲普遍的存在於很多Android開發心中的。優化

理解誤區主要體如今對如下幾個概念沒有區分清:spa

1,Activity的onDestory回調方法;線程

2,Activity的銷燬;htm

3,Activity的內存回收;對象

4,內存泄露;blog

5,Activity中動態註冊的BroadcastReceiver與Activity的引用持有關係。生命週期

 

下面咱們來一個個具體分析下。內存

1,Activity的onDestory回調方法。

onDestory做爲Activity中生命週期中的一個常見的方法,咱們先來看一下官方文檔中的描述。

從這個定義中,咱們得出以下幾點細節:

a,onDestory回調方法是Activity被銷燬前的最後一個Activity中回調方法;

b,onDestory回調方法的觸發時機是Activity被外部主動調用了finish()方法,或因系統內存空間不足而致使的臨行性的銷燬該Activity實例,以便得到內存空間。

在實際操做中,onDestory回調方法的觸發時機(或稱之爲Activity銷燬的觸發時機)主要表如今以下四種狀況:

a,人爲的主動的調用了finish()方法,以指望去銷燬當前的Activity;

b,人爲的主動操做致使的系統去銷燬當前Activity,如常見的按下手機上的返回鍵;

c,系統因內存不足致使的臨行性的銷燬該Activity實例,如從A Activity跳轉到B Activity後,系統內存不足的狀況下可能會銷燬掉A Activity;

d,打開手機上的「開發者選項」中的「不保留活動」選項,其中,這個更多的是爲了模擬出C場景,效果同C。

 

另外,上述的b中的按下手機上的返回鍵,系統源碼中也是調用了finish()方法。

區分上述的ab與cd兩種方式能夠經過isFinishing()方法的返回值來判斷。

爲了行文方便,且從ab與cd的人爲主觀性角度出發,本文將ab情形稱之爲「主動銷燬」,cd情形稱之爲「被動銷燬」。

 

2,Activity的銷燬。

相較於onDestory做爲的Activity生命週期中的回調方法,「銷燬」一詞在Activity中更多的表示的是Activity所處聲明週期中的一種「狀態」。

處於此種狀態的Activity實例,對於User Interface層來講是再也不可見的(不管是當前界面仍是按返回鍵等各類狀況)。

實踐中,處於「銷燬狀態」的Activity與上述的Activity銷燬的觸發時機具備一致的邏輯關係,這種邏輯關係具體體現爲:

a,對於主動銷燬,除卻Activity實例再也不可見外,當前Activity實例也直接被Activity棧中移除,直接表位爲對用戶操做導航路徑的影響;

b,對於被動銷燬,當前Activity實例依然再也不可見,但與主動銷燬不一樣的是,Activity實例的對應關係在Activity棧中依然存在,此時,對用戶操做導航路徑並沒有影響。

B Activity中,A Activity雖然被動銷燬,但未改變棧結構,按下返回鍵依然看到A,不夠此時的A與以前的A並不是一個Activity實例。

須要注意的是,處於「銷燬狀態」的Activity,嚴格意義上與當前Activity的真實內存佔用是否釋放沒有直接的對應關係。也就是說,Activity的銷燬,並不意味着Activity的內存就已經被回收。

 

3,Activity的內存回收。

Android是基於Java基礎之上,雖然在內存回收機制等方面作了必定的處理與優化(主要是基於Dalvik/ART),但針基本的GC原理上並沒有差異。主要表如今:

a,對於一個堆內存中的對象空間,一旦還有其餘的強引用可達,該內存空間就處於不可回收狀態;

b,GC的觸發時機依然具備不可肯定性,取決於系統依據當前的運行狀態與其系統自己的GC機制斷定進行。

也就是說,即便堆內存中對象已經處於可回收狀態,但只要GC未被觸發,內存依然被佔用。

在此,須要區分下GC的不可回收狀態與可回收狀態的區別,嚴格意義上來講,其並不是對立面,由於針對可回收狀態,還有可能對應的軟引用與弱引用須要加以考慮。

 

4,內存泄露。

Android中,內存泄露做爲一個基本的概念,經常被說起且實踐中也需儘可能掌握。網上關於內存泄露的文章林林總總。

終究內存泄露的本質,是指當前對象在實際運行中超出了其自己意義上生命週期範圍的,從而致使本該處於內存可回收狀態的但實際上卻一直處於不可回收狀態的內存佔用非正常現象。

內存泄露在出現,經常見於以下兩種狀況(爲行文方便,下述將發生了內存泄露的對象稱之爲M):

a,因異步回調中持有M,異步回調自己的生命時長長於M自己而致使的M發生內存泄露(如最多見的是Activity中的Handler以及異步線程致使Activity自己發生內存泄露);

b,因靜態屬性所指向的對象中持有了M而致使的M一直被強引用可達,使得M發生內存泄露(如最多見的單例對象中強引用了外部的非靜態對象)。

內存泄露過多會致使應用內存的不斷上升,達到必定程度會直接致使內存溢出(OOM)。具體解決內存泄露時,主要都是針對上述AB兩種狀況分析排查便可。

 

5,Activity中動態註冊的BroadcastReceiver與Activity的引用持有關係。

對於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的內存泄露。

相關文章
相關標籤/搜索