1. 程式人生 > >【Android基礎】Activity深入理解(二)——Activity棧和載入模式

【Android基礎】Activity深入理解(二)——Activity棧和載入模式

之前的一篇文章中詳細說明了Activity的生命週期,說明了Activity中的回撥方法是如何被觸發的。在使用者使用App時,每一個 Activity 都處於某一個狀態,對於開發者來說,是無法控制其應用程式處於某一個狀態的,這些均由系統來完成。那麼Activity的狀態又按照哪種邏輯來執行的呢?這就要了解一下Activity棧。

Activity棧

每一個Activity的狀態可以說都是由它所在的Activity棧中的位置所決定的。當一個新的Activity啟動時,當前的正在和使用者進行互動的Activity將會移動到棧頂。如果使用者按後退鍵或者結束前臺的Activity時,該Activity將會被移出棧而消亡,而此時棧頂的Activity將變為活動狀態。如下圖:
ActivityStack

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裡。
    以上。