天天看點

FLAG_ACTIVITY_CLEAR_TOP

今天解決一個比較有意思的問題

問題描述,假設有一個application中包含兩個activity A和B,此時先打開A 然後按Home鍵退回到MainHome,在framework中回退mainHome是通過startActivity方式打開mainHome的(我們的android platform是自己定制的,對按鍵重新設計,可能和原生系統不一樣),然後通過一個HotKey(就是一個外設的某一個按鍵)打開B,打開方式為StartActivity 設定兩個Flag 分别是 FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK,此時奇怪的事情就發生了,當B被打開後,我們按back鍵回退,回退的地方竟然是A 而不是MainHome。如果我們換一種打開B的方式,不設定上面的兩個Flag,那麼從B就可以正常回退到MainHome,那麼問題顯然是出在了兩個設定标志的地方上。然後翻閱文檔和API。

将資料貼上:

FLAG_ACTIVITY_NEW_TASK:

一個Activity一般通過調用startActivity()啟動并加入到Task中。它同調用者一樣,進入同一個Task。

   然而,如果傳遞給startActivity()的Intent對象中包含FLAG_ACTIVITY_NEW_TASK時,系統會搜尋一個新的Task來容納新的Activity。

   通常,如标志的名字所示,是一個新的Task。然而,并不是必須是。如果已經存在一個Task與新Activity的affinity相同,這個Activity就會加入到那個Task中。如果不是,啟動一個新的Task。

  如果啟動它的acitve和新Activity的affinity相同,那麼新Activity的會進入啟動它的acitve所在的Task.

FLAG_ACTIVITY_CLEAR_TOP:

 如果設定,并且這個Activity已經在目前的Task中運作,是以,不再是重新啟動一個這個Activity的執行個體,而是在這個Activity上方的所有Activity都将關閉,然後這個Intent會作為一個新的Intent投遞到老的Activity(現在位于頂端)中。

    例如,假設一個Task中包含這些Activity:A,B,C,D。如果D調用了startActivity(),并且包含一個指向Activity B的Intent,那麼,C和D都将結束,然後B接收到這個Intent,是以,目前stack的狀況是:A,B。

    上例中正在運作的Activity B既可以在onNewIntent()中接收到這個新的Intent,也可以把自己關閉然後重新啟動來接收這個Intent。如果它的啟動模式聲明為 “standard”(預設值),

    并且你沒有在這個Intent中設定FLAG_ACTIVITY_SINGLE_TOP标志,那麼它将關閉然後重新建立;對于其它的啟動模式,或者在這個Intent中設定FLAG_ACTIVITY_SINGLE_TOP标志,都将把這個Intent投遞到目前這個執行個體的onNewIntent()中。

    這個啟動模式還可以與FLAG_ACTIVITY_NEW_TASK結合起來使用:用于啟動一個Task中的根Activity,它會把那個Task中任何運作的執行個體帶入前台,然後清除它直到根Activity。這非常有用,例如,當從Notification Manager處啟動一個Activity

也就是說對我們上例啟決定性作用的是FLAG_ACTIVITY_NEW_TASK這個flag.

原因是,當我們的打開B時,因為使用了new_task屬性,是以系統就會為B尋找一個最具

affinity的activity(也就是A)如果找到了,系統會将此activity(也就是A)設定為前台狀态,就是background,然後将B歸并到A的Task中去,并且壓入頂(如果B之前已經存在于A的Task中那麼CLEAR_TOP屬性将會把B之上得所有activity清除掉,然後将B壓入棧頂).問題分析到這裡,應該已經很清楚了,也就是說A和B一定具有相同的affinity,查閱文檔發現,因為A和B存在于同一個.apk下也就是同一個package下,是以預設具有相同的affinity.下面貼上affinity的相關文檔:

    什麼是Affinity

在某些情況下,Android需要知道一個Activity屬于哪個Task,即使它沒有被啟動到一個具體的Task裡。這是通過任務共用性(Affinities)完成的。任務共用性(Affinities)為這個運作一個或多個Activity的Task提供了一個獨特的靜态名稱,預設的一個活動的任務共用性(Affinity)是實作了該Activity的.apk包的名字。

當開始一個沒有 Intent.FLAG_ACTIVITY_NEW_TASK标志的Activity時,任務共用性affinities不會影響将會運作該新活動的 Task:它總是運作在啟動它的Task裡。但是,如果使用了NEW_TASK标志,那麼共用性(affinity)将被用來判斷是否已經存在一個有相同共用性(affinity)的Task。如果是這樣,這項Task将被切換到前面而新的Activity會啟動于這個Task的頂層。

這種特性在您必須使用NEW_TASK标志的情況下最有用,尤其是從狀态欄通知或桌面快捷方式啟動活動時。結果是,當使用者用這種方式啟動您的應用程式時,它的目前Task将被切換到前台,而且想要檢視的Activity被放在最上面。

你可以在程式清單(Manifest)檔案的應用程式application标簽中為.apk包中所有的活動配置設定你自己的任務共用性Affinites,或者在活動标記中為各個活動進行配置設定。

在前面的文章“Android四種Activity的加載模式”我們提到:Activity的加載模式受啟動Activity的Intent對象中設定的Flag和manifest檔案中Activity的<activity>元素的特性值互動控制。

跟 Task 有關的 manifest檔案中Activity的特性值介紹

android:allowTaskReparenting

    用來标記Activity能否從啟動的Task移動到有着affinity的Task(當這個Task進入到前台時)

  “true”,表示能移動,“false”,表示它必須呆在啟動時呆在的那個Task裡。

    如果這個特性沒有被設定,設定到<application>元素上的allowTaskReparenting特性的值會應用到Activity上。預設值為“false”。

    一般來說,當Activity啟動後,它就與啟動它的Task關聯,并且在那裡耗盡它的整個生命周期。當目前的Task不再顯示時,你可以使用這個特性來強制Activity移動到有着affinity的Task中。典型用法是:把一個應用程式的Activity移到另一個應用程式的主Task中。

    例如,如果 email中包含一個web頁的連結,點選它就會啟動一個Activity來顯示這個頁面。這個Activity是由Browser應用程式定義的,但是,現在它作為email Task的一部分。如果它重新宿主到Browser Task裡,當Browser下一次進入到前台時,它就能被看見,并且,當email Task再次進入前台時,就看不到它了。

   Actvity的affinity是由taskAffinity特性定義的。Task的affinity是通過讀取根Activity的affinity 決定。是以,根Activity總是位于相同affinity的Task裡。由于啟動模式為“singleTask”和“singleInstance”的Activity隻能位于Task的底部,是以,重新宿主隻能限于“standard”和“singleTop”模式。

android:alwaysRetainTaskState

    用來标記Activity所在的Task的狀态是否總是由系統來保持。

    “true”,表示總是;“false”,表示在某種情形下允許系統恢複Task到它的初始化狀态。預設值是“false”。

    這個特性隻針對Task的根Activity有意義;對其它Activity來說,忽略之。 

    一般來說,特定的情形如當使用者從主畫面重新選擇這個Task時,系統會對這個Task進行清理(從stack中删除位于根Activity之上的所有Activivity)。典型的情況,當使用者有一段時間沒有通路這個Task時也會這麼做,例如30分鐘。

    然而,當這個特性設為“true”時,使用者總是能回到這個Task的最新狀态,無論他們是如何啟動的。這非常有用,例如,像Browser應用程式,這裡有很多的狀态(例如多個打開的Tab),使用者不想丢失這些狀态。

       是以最終的解決方式就是為B或者A設定不同的affinity這樣A 就不會被吸引到background狀态了,而B 也會在另外的Task中打開。在B中按back鍵就會傳回到MainHome界面。

繼續閱讀