【Android基礎】Activity深入理解(二)——Activity棧和載入模式
之前的一篇文章中詳細說明了Activity的生命週期,說明了Activity中的回撥方法是如何被觸發的。在使用者使用App時,每一個 Activity 都處於某一個狀態,對於開發者來說,是無法控制其應用程式處於某一個狀態的,這些均由系統來完成。那麼Activity的狀態又按照哪種邏輯來執行的呢?這就要了解一下Activity棧。
Activity棧
每一個Activity的狀態可以說都是由它所在的Activity棧中的位置所決定的。當一個新的Activity啟動時,當前的正在和使用者進行互動的Activity將會移動到棧頂。如果使用者按後退鍵或者結束前臺的Activity時,該Activity將會被移出棧而消亡,而此時棧頂的Activity將變為活動狀態。如下圖:
Activity的狀態
一般來說Activity可以分為4種狀態:
- 執行:當一個Activity在棧頂時,它是有焦點的,可與使用者互動的。系統會盡可能的保持它的活動狀態,甚至銷燬其他Activity來確保當前的Activity有足夠的資源可用。當另外一個Activity被啟用的時候,這個Activity會被暫停。
- 暫停:當一個Activity可視但是沒有焦點時,那麼該Activity就進入了暫停狀態。此時該Activity仍然是可執行狀態,只不過不可以與使用者互動。但是在一些特殊情況下,如記憶體不足時,系統會選擇銷燬一個或多個暫停狀態的Activity來為當前執行的Activity提供足夠的資源。當一個Activity被完全隱藏,它將程式設計停止狀態。
- 停止:當一個Activity是不可見的,它就進入了停止狀態,但是這個Activity仍在記憶體中保有它大的所有狀態。但是當其他的地方需要記憶體時,它將是最有可能被銷燬的。一旦一個Activity被關閉或者退出,那麼它將成為待用狀態。
- 待用:在一個Activity被銷燬到被裝載前,它都是處於待用狀態的。待用狀態的Activity不再Activity棧中。
下面我們建立三個Activity,FirstActivity,SecondActivity,ThirdActivity。這三個Activity中都有一個按鈕,點選FirstActivity中的按鈕可以跳轉到SecondActivity;SecondActivity中的可以跳轉到ThirdActivity;ThirdActivity中的可以跳轉到撥打電話介面 。這樣我們的程式事實上可以建立四個Activity。具體例項詳見。
我們在這三個Activity中都實現了onCreate,onStop,onPause,onRestart,onDestroy方法,每個方法都有一條輸出語句用來表示當前Activity所處的狀態。
我們執行程式,進入FirstActivity,檢視控制檯,FirstActivity的onCreate正常執行。點選按鈕,跳轉到SecondActivity,發現FirstActivity的onPause,onStop方法正常執行,SecondActivity也執行了onCreate方法。點選按鈕跳轉到ThirdActivity時,發現SecondActivity執行了onDestroy方法。因為我們在SecondActivity的startActivity方法後執行了Finish方法,從而結束了SecondActivity。仙子按返回鍵,程式直接跳轉到了FirstActivity中。說明SecondActivity已經從Activity棧中移除。
ActivityStackDemo詳解:
- 應用程式啟動之後,執行的第一個 Activity ( FirstActivity ),該 FirstActivity 物件被壓入到 Stack 當中。
當點選了 FirstActivity 的按鈕之後,啟動第二個 Activity ( SecondActivity ),該 SecondActivity 物件被壓入到 Stack 當中
注:現在 FirstActivity 處在 Stack 的底部, ThirdActivity 處於 Stack 的頂部,手機永遠顯示的都是 Stack 頂部的元素,所以現在介面顯示的是 ThirdActivity 。
最後點選 ThirdActivity 的按鈕之後,啟動 Android 自帶的通訊的應用程式,該PhoneCall Activity 物件被壓入到 Stack 當中。
注:同理,現在 FirstActivity 仍然處在 Stack 的底部, PhoneCall Activity 物件處於 Stack 的頂部,所以現在介面顯示的是 PhoneCall Activity 物件的介面。
(以上執行的是壓棧操作,現在我們點選返回來看彈棧操作)點選模擬器右側的 Back 按鈕之後,這個時候 PhoneCall Activity 物件被從棧中彈出來。 PhoneCall Activity 物件被彈出來之後, ThirdActivity 又變成棧的頂部,當前程式顯示的就是 ThirdActivity 的內容。(因為棧遵循後進先出的原則)
再點選 Back 按鈕, ThirdActivity 被從棧中彈出來,程式顯示 FirstActivity 的內容。
這裡筆者特別提醒一下:在整個棧當中, Activity 只有彈出和壓入這兩個動作,是不允許調換 Activity 之間的順序的。Activity的載入模式
在Android的多Activity開發中,Activity之間的跳轉可能需要有多種方式,有時是普通的生成一個新例項,有時希望跳轉到原來某個Activity例項,而不是生成大量的重複的Activity。載入模式便是決定以哪種方式啟動一個跳轉到原來某個Activity例項。
在Android裡,有4種Activity的啟動模式,分別為:
- standard: 標準模式,一呼叫startActivity()方法就會產生一個新的例項。
- singleTop: 如果已經有一個例項位於Activity棧的頂部時,就不產生新的例項,而只是呼叫Activity中的newInstance()方法。如果不位於棧頂,會產生一個新的例項。
- singleTask: 會在一個新的task中產生這個例項,以後每次呼叫都會使用這個,不會去產生新的例項了。
singleInstance: 這個跟singleTask基本上是一樣,只有一個區別:在這個模式下的Activity例項所處的task中,只能有這個activity例項,不能有其他的例項。
這些啟動模式可以在功能清單檔案AndroidManifest.xml中進行設定,中的launchMode屬性。
相關的程式碼中也有一些標誌可以使用,比如我們想只啟用一個例項,則可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 標誌,這個標誌表示:如果這個Activity已經啟動了,就不產生新的Activity,而只是把這個Activity例項加到棧頂來就可以了。
Intent intent = new Intent(MainActivity.this, AnotherActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Activity的載入模式受啟動Activity的Intent物件中設定的Flag和manifest檔案中Activity的元素的特性值相互控制。
常用的Intent Flag有:
FLAG_ACTIVITY_NEW_TASK:有Activity A,B。A通過intent跳轉到B,並且這個intent添加了FLAG_ACTIVITY_NEW_TASK 標記,如果B這個Activity在Manifest.xml中的宣告中添加了Task affinity(親和性),並且和A所在的棧的affinity不同。那麼系統首先會找有沒有已存在的棧和B的affinity相同。如果存在則把B壓進該棧中。如果B的affinity沒有設定,那麼B則會被直接壓入A的棧中。注意如果試圖從非Activity的非正常途徑啟動一個Activity,比如從一個Service中啟動一個Activity,則intent必須要新增FLAG_ACTIVITY_NEW_TASK 標記。
FLAG_ACTIVITY_CLEAR_TOP:在一個棧中有ActivityA,B,現在A通過intent跳轉至B,如果這個intent新增FLAG_ACTIVITY_CLEAR_TOP 標記,那麼A,B都會被從這個棧中移除。如果沒有加FLAG_ACTIVITY_CLEAR_TOP 那麼,系統會重新建立一個B的例項壓入到該棧中。這跟上面把B的Launch mode設定成singleTask類似。
FLAG_ACTIVITY_SINGLE_TOP:和上面Activity的 Launch mode的singleTop類似。如果某個intent添加了這個標誌,並且這個intent的目標Activity就是棧頂的Activity,那麼將不會新建一個例項壓入棧中。
四種載入模式的區別
是否允許有多個例項:
“standard”和”singleTop”可以被例項化多次,並且是可以存在於不同的task(棧)中;這種例項化時一個task可以包括一個activity的多個例項;
“singleTask”和”singleInstance”則限制只生成一個例項,並且是task的根元素。singleTop 要求如果建立intent的時候棧頂已經有要建立的Activity的例項,則將intent傳送給該例項,而不建立新的例項。另外,“singleInstance”獨佔一個task,其它Activity不能存在那個task裡;如果它啟動了一個新的Activity,不管新的Activity的launch mode 如何,新的Activity都將會到別的task裡執行(如同加了FLAG_ACTIVITY_NEW_TASK引數)。而另外三種模式,則可以和其它Activity共存。
是否每次都生成新例項
“standard”對於每一個啟動Intent都會生成一個Activity的新例項;“singleTop”的activity如果在task的棧頂的話,則不生成新的該Activity的例項,直接使用棧頂的例項,否則,生成該Activity的例項。
“singleInstance”是其所在棧的唯一Activity,它會每次都被重用。
“singleTask” 如果在棧頂,則接受intent,否則,該intent會被丟棄,但是該task仍會回到前臺。 當已經存在的Activity例項處理新的intent時候,會呼叫onNewIntent()方法,如果收到intent生成一個activity例項,那麼使用者可以通過back鍵回到上一個狀態;如果是已經存在的一個activity來處理這個intent的話,使用者不能通過按back鍵返回到這之前的狀態。
所屬task的區別
一般情況下,“standard”和”singleTop”的Activity的目標task,和收到的Intent的傳送者在同一個task內,就相當於誰呼叫它,它就跟誰在同一個Task中。除非Intent包括引數FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK引數,會啟動到別的task裡。
“singleTask”和”singleInstance” 總是把要啟動的Activity作為一個task的根元素,他們不會被啟動到一個其他task裡。
以上。