1. 程式人生 > >Activity的生命週期和啟動模式再解析

Activity的生命週期和啟動模式再解析

目錄

前言

前言

這篇文章寫於幾個月之前,當時有事給耽擱了,放在草稿箱裡居然給忘了,今天翻了翻部落格才想起來,連忙抽時間補全了。這是一篇基礎的理論知識,看起來會有點枯燥,相信很多朋友也都是瞭解的,內容呢,主要是來自主席的《安卓開發藝術探索》,來深入總結一下Activity的生命週期和啟動模式。

一、Activity的生命週期再解析

(一)典型情況下的生命週期

1、有過Android開發基礎的朋友們都知道,在正常情況下,Activity會經歷如下生命週期:

(1)onCreate:表示Activity正在被建立,這是生命週期的第一個方法,在這個方法中,我們可以做一些初始化的工作,比如:呼叫setContentView去載入介面佈局資源,做一些資料的初始化工作等。

(2)onRestart:表示Activity正在重新啟動,噹噹前Activity從不可見重新變為可見狀態時,onRestart就會被呼叫。常見場景:比如使用者按Home鍵切換到桌面或者重新開啟一個新的Activity,此時原有的Activity就會被暫停,當用戶再次返回原Activity時,就會呼叫onRestart方法。

(3)onStart:表示Activity正在被啟動,即將開始,這時Activity已經可見了,只不過還沒有出現在前臺,無法和使用者進行互動,此時Activity還處在後臺,說白了就是我們還看不到。

(4)onResume:表示Activity已經可見了,並且出現在前臺,可以和使用者進行互動,人眼是可見的。

(5)onPause:表示Activity正在停止,正常情況下,緊接著onStop就會被呼叫,在這種情況下,如果此時快速的再回到當前Activity,則onResume還會被呼叫,但是這屬於極端情況,使用者很難復現這一場景。在這個方法中,可以做一些儲存資料、停止動畫等工作,需要注意的是,該方法中的這些處理工作不能太耗時,否則會影響新Activity的顯示。(當年剛去杭州找的第一份工作就被當時的面試官問到了這個問題,當時還是個新手,和麵試官探討了一會才有了更深入的理解)

(6)onStop:表示Activity即將停止,可以做一些稍微重量級的回收工作,注意是稍微,同樣的道理,不能太耗時。

(7)onDestroy:表示Activity即將被銷燬,這是生命週期中的最後一個回撥方法,在該方法中,我們可以做一些回收工作和最終的資源釋放。

正常情況下,Activity的常用生命週期就只有上面7個,具體切換過程如下圖所示:

總結:關於以上這些內容,最好的方法就是自己寫一個Demo,然後重寫這些生命週期方法,在方法內部打上日誌,自己跑一邊程式,模擬一下可能發生的場景,然後根據輸出日誌的先後順序來理解生命週期的先後順序。

(二)異常情況下的生命週期

1、資源相關的系統配置發生改變導致Activity被殺死並重新建立

分析:在預設情況下,如果Activity不做特殊處理,當系統配置發生改變後,Activity就會被銷燬並重新建立。可能的場景:比如手機橫豎屏切換時,就是系統配置發生了改變。它的onPause、onStop、onDestroy均會被呼叫,同時由於它是在異常情況下被終止的,所以系統還會呼叫onSaveInstanceState來儲存當前Activity的狀態。這個方法呼叫時機在onStop之前,並且正常情況下該方法不會被呼叫。當Activity被重新建立以後,系統會呼叫onRestoreInstanceState,並且把Activity銷燬時onSaveInstanceState方法所儲存的Bundle物件作為引數同時傳遞給onRestoreInstanceState和onCreate方法。因此我們可以通過這兩個方法中的Bundle引數是否為空來判斷Activity是否被重建了,如果被重建了,可以取出之前儲存的資料並回復,從時序上看,onRestoreInstanceState在onStart之後呼叫。

2、資源記憶體不足導致低優先順序的Activity被殺死

這種情況的資料儲存和恢復過程和第一種情況完全一致,但是場景不好模擬。這裡說一下Activity的優先順序情況。Activity按照優先順序從高到低可以分為如下三種:

1、前臺Activity——正在和使用者互動的Activity,優先順序最高;

2、可見但非前臺Activity——比如Activity中彈出了一個對話方塊,導致Activity可見但是位於後臺無法和使用者直接互動;

3、後臺Activity——已經被暫停的Activity,比如執行了onStop,優先順序最低。

當系統記憶體不足時,系統會按照上述優先順序去殺死目標Activity所在的程序,並在後續通過onSaveInstanceState和onRestoreInstanceState來儲存和恢復資料。如果一個程序中沒有四大元件在執行,那麼這個程序將很快被殺死,因此一些後臺工作不適合脫離四大元件獨自執行在後臺中,這樣很容易被殺死。比較好的方法是將後臺工作放入Service中從而保證程序有一定的優先順序,這樣就不會輕易被系統殺死。

問題1:當系統配置發生改變後,Activity會被重新建立,如何才能不被重建呢?

答:可以給Activity指定configChanges屬性,指定多個值時,可以用 “|” 連線起來,比如:android:configChanges="orientation|keyboardHidden"。具體每個值的含義如下表中所示:

二、Activity的啟動模式

(一)Activity的啟動模式(LaunchMode)

1、Activity為什麼需要啟動模式?

答:預設情況下,多次啟動同一個Activity,系統會建立多個例項並把它們一一放入任務棧中,當我們按返回鍵的時候,這些Activity會一一回退。那麼問題來了,這樣不是很傻嗎?一樣的東西為什麼每次都要重新創建出來呢?所以設計Android系統的時候,就已經考慮到了這個問題,所以提供了啟動模式來修改系統的預設行為。目前啟動模式有四種,分別是:standard、singleTop、singleTask、singleInstance。

2、standard:標準模式(系統的預設模式)

每次啟動一個Activity都會重新建立一個新的例項,不管這個例項是否已經存在。被建立的例項的生命週期符合典型情況下的生命週期。在這種模式下,誰啟動了這個Activity,那麼Activity就執行在啟動它的那個Activity所在的棧中。有這樣一種情況相信你曾經遇到過,使用ApplicationContext啟動標準模式的Activity會報錯,如下所示:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag.Is this really what you want?

這就是因為標準模式的Activity預設會進入啟動它的Activity所屬的任務棧中,但是由於非Activity型別的Context並沒有任務棧,所以報錯了,解決這個問題的方法就是為待啟動的Activity指定FLAG_ACTIVITY_NEW_TASK標記位。

3、singleTop:棧頂複用模式

這種模式下,如果新的Activity已經位於任務棧的棧頂,那麼此Activity不會被重新建立,同時它的onNewIntent方法會被回撥,通過此方法的引數我們可以取出當前請求的資訊。如果新Activity的例項已經存在但不是位於棧頂,那麼新Activity仍然會重新建立。

4、singleTask:棧內複用模式

這是一種單例項模式,在這種模式下,只要Activity在一個棧中存在,那麼多次啟動此Activity都不會重新建立例項,和singleTop一樣,系統也會回撥它的onNewIntent方法。具體點來說,當一個具有singleTask模式的Activity請求啟動後,系統首先尋找是否存在它想要的任務棧,如果不存在,則建立任務棧建立例項入棧;如果存在任務棧,則查詢這個Activity例項是否存在,若不存在建立後入棧,若存在,則將它置於棧頂(它之上的Activity都會被pop出棧)並且回撥它的onNewIntent方法。舉幾個例子:

5、singleInstance:單例項模式

這是一種加強的singleTask模式,它除了具有singleTask模式的所有特性外,還加強了一點,就是具有這種模式的Activity只能單獨的位於一個任務棧中,換句話說,如果Activity A是這種模式,當A啟動後,系統會為它建立一個新的任務棧,然後A獨自在這個任務棧中,由於棧內複用的特性,後續的請求均不會建立新的Activity,除非這個獨特的任務棧被系統銷燬了。

問題1:什麼是Activity所需的任務棧?

答:這要從一個引數說起:TaskAffinity——任務相關性。這個引數標識了一個Activity所需要的任務棧的名字,預設情況下,所有Activity所需的任務棧的名字是應用的包名。當然,我們可以為每個Activity單獨指定TaskAffinity屬性,這個屬性值不能和包名相同,否則沒有任何實際意義,等同於沒指定。TaskAffinity主要和singleTask啟動模式或者allowTaskReparenting屬性配對使用,在其他情況下沒有意義。任務棧分為前臺任務棧和後臺任務棧,後臺任務棧中的Activity處於暫停狀態,使用者可以通過切換將後臺任務棧再次調到前臺。下面對兩種配對的使用加以解釋說明,如下圖所示:

問題2:如何給Activity指定啟動模式呢?

答:有兩種方法,如下圖:

(二)Activity的Flags

Activity的Flags很多,這裡說一些比較常用的標記位。標記位的作用很多,有的標記位可以設定為Activity的啟動模式,比如:FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;有的標記位可以影響Activity的執行狀態,比如:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。下面具體介紹幾個常用的標記位:

1、FLAG_ACTIVITY_NEW_TASK

這個標記位的作用是為Activity指定“singleTask”啟動模式,效果和在XML中指定該啟動模式相同。

2、FLAG_ACTIVITY_SINGLE_TOP

這個標記位的作用是為Activity指定“singleTop”啟動模式,效果和在XML中指定該啟動模式相同。

3、FLAG_ACTIVITY_CLEAR_TOP

具有這個標記位的Activity,當它啟動時,在同一個任務棧中所有位於它上面的Activity都要出棧。這個標記位一般會和singleTask啟動模式一起出現,在這種情況下,被啟動Activity的例項如果已經存在,那麼系統就會呼叫它的onNewIntent。如果被啟動Activity採用standard模式啟動,那麼連同它之上的Activity都要出棧,系統會建立新的Activity例項並放入棧頂。

4、FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有這個標記位的Activity不會出現在歷史Activity的列表中,當某些情況下我們不希望使用者通過歷史列表回到我們的Activity的時候這個標記比較有用。它等同於在XML中指定Activity的屬性android:excludeFromRecents="true"

以上內容基本都是來自《安卓開發藝術探索》,重新總結一遍是為了加深理解,以後有忘記的時候可以很方便的找到,這也是寫博文的一個好處吧,今天就寫到這裡,加油everybody!