1. 程式人生 > >android Activity 堆疊和親屬關係

android Activity 堆疊和親屬關係

在android中,一個activity元件可以啟用另一個activity元件(可能屬於另一個應用程式)。

    若新的被啟用的activity元件屬於另一個應用程式,則那個activity元件會執行在那個應用程式的程序中,但是從使用者的角度來看,好像就是屬於本應用程式一樣。Android是通過將之前的activity元件和新被啟用的activity元件放入同一個任務棧來實現這個功能的。從使用者的角度看,一個任務棧就代表了“一個應用程式”。它實際上是一個棧,裡面放著一組被排列好的相關的activity元件。位於棧底的activity(根activity)就是開啟這個任務棧的activity元件,一般情況下,就是應用程式的主介面。而位於棧頂的activity元件即代表當前被啟用的activity元件(可接收使用者行為的activity)。

    任務棧中包含了activity元件的物件,且任務棧中可以包含有某一個activity元件型別的多個例項物件。在任務棧中的activity元件不能被重排序,只能被壓棧和彈棧。

    任務棧不是某個型別,也不是某一個元素,它是一組activity元件的組織形式。所以沒有辦法在不影響任務棧中的activity元件的情況下,單獨設定任務棧的引數。根activity的引數既是整個任務棧的引數,它會影響任務棧中的所有activity元件。

    當某個應用程式在前後臺切換的時候,實際上就是代表這個應用程式的一個任務棧在前後臺切換。

    剛剛描述的行為是activity和任務棧的預設行為,但也有辦法在很多方面對它進行修改:

    方法1:在傳送的請求(即Intent物件)中設定一些標記。

    方法2:在manifest檔案中,對接收請求(即Intent物件)的activity元件設定一些屬性。

    所以在請求者和接收者中都可以進行控制。

在Intent物件中主要的標誌有:

    FLAG_ACTIVITY_NEW_TASK

    FLAG_ACTIVITY_CLEAR_TOP

    FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

    FLAG_ACTIVITY_SINGLE_TOP

在<activity>標籤中,主要的屬性有:

    taskAffinity   android:taskAffinity="com.cardroid.sdhc"  表示兩個應用裡面的親屬關係,如果一個應用的某個ACTIVITY和另一個應用的ACTIVITY設定這個屬性
    然後,這兩個ACTIVITY顯示後點擊HOME鍵盤,從一個應用啟動就會顯示點選HOME的那個ACTIVITY

    launchMode

    allowTaskReparenting

    clearTaskOnLaunch

    alwaysRetainTaskState

    finishOnTaskLaunch

    接下來的內容就會講解一些Intent標誌和<activity>標籤屬性的作用和用法。

1.親屬關係和新的任務

    預設情況下,一個應用程式中的activity元件彼此之間是親屬關係――也就是說它們屬於同一個任務棧。但是我們可以通過設定某個<activity>標籤的taskAffinity屬性來為這個activity元件設定親屬關係。在不同的應用程式中定義的activity元件可以共用同一個親屬關係,或者在同一個的應用程式中定義的activity元件可以使用不同的親屬關係。親屬關係會在兩種情況下發揮作用:

    1)負責啟用activity元件的Intent物件中包含了FLAG_ACTIVITY_NEW_TASK標誌。

    2)被啟用的activity元件的allowTaskReparenting屬性被設定為“true”。

關於FLAG_ACTIVITY_NEW_TASK標誌量

    預設情況下,一個被啟用的新activity會和負責啟用它的那個activity元件存在於同一個任務棧中。但是若負責啟用的Intent物件包含了FLAG_ACTIVITY_NEW_TASK標誌,則系統會為存放那個即被啟用的新activity尋找一個新的任務棧。此時,若已經存在了相同親屬關係的任務棧,則系統會直接將這個即被啟用的新activity放入到這個任務棧中;否則系統會開始一個新的任務棧。

關於allowTaskReparenting屬性

    若一個activity元件的allowTaskReparenting被置為“true”,則當與這個activity有相同的親屬關係的任務棧被切換到前臺的時候,這個activity會從當前存在的任務棧中移動到與其有相同的親屬關係的任務棧中。

    若從使用者的角度來看,一個.apk檔案包含了一個以上的“應用程式”,那你可能要為那些activity元件指定不同的親屬關係。

2.啟動模式

<activity>標籤的launchMode屬性可以設定為四種不同的模式:

    “standard”(預設模式)

    “singleTop”
 android:launchMode="singleTop" 相當於每次都從這個ACITIVITY啟動
    “singleTask”
 android:launchMode="singleTop" 和singleTop類似,不同於,每次啟動後都在最上面
    “singleInstance”

    這幾種模式的區別體現以下四點上:

    1)當這個activity被啟用的時候,會放入哪個任務棧。

    對於“standard”和“singleTop”模式,這個新被啟用的activity會放入和之前的activity相同的任務棧中――除非如前所述,Intent物件包含FLAG_ACTIVITY_NEW_TASK標誌。

    但“singleTask”和“singleInstance”模式則表示這個新被啟用的activity不會放入已經存在的任務棧中,它會重新開啟一個任務棧,並作為這個新的任務棧的根activity。

    2)是否可以存在這個activity型別的多個例項。

    對於“standard”和“singleTop”模式,可以有多個例項,並且這些例項可以屬於不同的任務棧,每個任務棧也可以包含有這個activity型別的多個例項。

    但“singleTask”和“singleInstance”模式則表示至多隻可以存在這個activity型別的一個例項。又因為有第一點必須是根activity的限制,所以這意味著在同一時間,在手機上絕不會存在多於一個的由這個activity啟動的任務棧。

    3)包含此activity的任務棧是否可以包含其它的activity。

    “singleInstance”模式表示包含此activity的任務棧不可以包含其它的activity。若此activity啟動了另一個activity元件,那麼無論那個activity元件的啟動模式是什麼或是Intent物件中是否包含了FLAG_ACTIVITY_NEW_TASK標誌,它都會被放入另外的任務棧。在其它方面“singleInstance”模式和“singleTask”模式是一樣的。

    其餘三種啟動模式則允許包含此activity的任務棧包含其它的activity。

    4)Whether a new instance of the class will be launched to handle a new intent.

    對於預設的“standard”模式,每當響應一個Intent物件,都會建立一個這種activity型別的新的例項。即每一個activity例項處理一個intent。

    對於“singleTop”模式,只有當這個activity的例項當前處於任務棧的棧頂位置,則它會被重複利用來處理新到達的intent物件。否則就和“standard”模式的行為一樣。

    正如第二點所說的,“singleTask”和“singleInstance”模式表示只能有一個例項,所以這個唯一的例項需要處理所有新到達的intent物件。又由於“singleInstance”模式的activity例項總是位於任務棧的棧頂,所以這樣做很正常。但對於“singleTask”模式的acitvity,在其上面可能存在其它的activity元件,所以它的位置並不是棧頂,在這種情況下,intent物件會被丟棄。(雖然會被丟棄,但是這個intent物件會使這個任務棧切換到前臺)

    如果一個新到達的intent物件是被一個已經存在的activity元件來處理的,那麼這個activity的onNewIntent(android.content.Intent)方法會被系統呼叫。

    注意:若為了處理一個新到達的intent物件而建立了一個activity例項,則使用者按下“BACK”鍵就會退到之前的那個activity。但若這個新到達的intent物件是由一個已經存在的activity元件來處理的,那麼使用者按下“BACK” 鍵就不會回退到處理這個新intent物件之前的狀態了。

3.清理任務棧

    如果一個任務棧在很長的一段時間都被使用者保持在後臺的,那麼系統就會將這個任務棧中除了根activity以外的其它所有activity全部清除掉。從這之後,當用戶再將任務棧切換到前臺,則只能顯示根activity了。

以上說的是預設模式,可以通過<activity>標籤的一些屬性來更改:

    1)alwaysRetainTaskState屬性

    如果將根activity的alwaysRetainTaskState屬性設定為“true”,則即便一個任務棧在很長的一段時間都被使用者保持在後臺的,系統也不會對這個任務棧進行清理。

    2)clearTaskOnLaunch屬性

    如果將根activity的clearTaskOnLaunch屬性設定為“true”,那麼只有這個任務棧切換到了後臺,那麼系統就會將這個任務棧中除了根activity以外的其它所有activity全部清除掉。即和alwaysRetainTaskState的行為完全相反。

    3) finishOnTaskLaunch屬性

    這個屬性的行為類似於clearTaskOnLaunch,但是此屬性作用於單個的activity物件,而不是整個任務棧。當這個任務棧切換到了後臺,這個屬性可以使任務棧清理包括根activity在內的任何activity物件。

    這裡也有另一種方法來使activity物件從任務棧中被移除。若Intent物件包含FLAG_ACTIVITY_CLEAR_TOP標誌,並且在目標任務棧中已經存在了用於處理這個Intent物件的activity型別的一個例項,那麼在任務棧中這個例項之上的所有activity例項會被移除。從而用於處理這個Intent物件的activity型別的那個例項會位於任務棧的棧頂,並用來處理那個Intent物件。若那個匹合的activity型別的啟動模式是“standard”,則這個已經存在於任務棧中的匹合的activity型別的例項也會被移除,並且一個新的此型別activity的例項被建立並壓棧來處理這個Intent物件。

    FLAG_ACTIVITY_CLEAR_TOP這個標誌經常和FLAG_ACTIVITY_NEW_TASK標誌結合使用,這樣結合使用的意思是在另一個任務棧中定位已經存在的匹合的activity型別的例項,並且讓此例項位於棧頂。

4.啟動任務棧

    通過將一個activity型別的intent-filter的動作設定為“android.intent.action.MAIN”,類別設定為“android.intent.category.LAUNCHER”可以使這個activity例項稱為一個任務棧的入口。擁有這種型別的intent-filter的activity型別的圖表和名字也會顯示在application launcher中。

    第二個能力是很重要的:使用者必須能夠使一個任務棧切換到後臺,也可以隨時將其切換到前臺。出於這個原因,使activity在啟動時新開任務棧的啟動模式(即“singleTask”和“singleInstance”模式)只應該被利用在擁有擁有“android.intent.action.MAIN”動作和“android.intent.category.LAUNCHER”類別的intent-filter的activity型別上。

    類似的限制同樣體現在FLAG_ACTIVITY_NEW_TASK標誌上。如果這個標誌使一個activity開始了一個新的任務棧,並且使用者點選“HOME”鍵將其切換到了後臺,則必須有某種方式使使用者可以重新將那個任務棧切換到前臺。一些例項(比如通知管理器),總是在外部的任務棧中開啟一個activity,而不是其自身的任務棧,所以它們總是將FLAG_ACTIVITY_NEW_TASK標誌放入Intent物件中,並將Intent物件傳入startActivity()方法中。

    對於在某些情況下,你不希望使用者能夠返回到某一個activity,那麼可以通過設定<activity>標籤的“finishOnTaskLaunch”屬性為“true”來實現。