1. 程式人生 > >Android Tasks、ActivityStack和Activity

Android Tasks、ActivityStack和Activity

最近又在開展一些Android的開發工作,出現了在個別場景下發生的Activity切換不符合預期的情況,於是來溫習一下Android的Tasks、ActivityStack、Activity,以及影響到他們的幾個重要引數launchMode、taskAffinity、IntentFlags

 

應用程式的程序與執行緒

從應用程式的角度來看,應用程式一般定義了應用對應的程序,以及上面執行著的一系列的執行緒。開發人員進行debug的時候的關注點會是哪個程序的哪個執行緒的哪個stacktrace。

 

Android四大元件

從Android應用程式的角度來看,除了程序和執行緒的這一個角度,從程式裡面詳細類的組織來看,Android應用程式包含有Activity、Service、ContentProvider、Broadcast Receiver4大元件,這四大元件就是通過我們之前說過的

XML外掛方式配置到AndroidManifest當中的。而這其中,Activity元件是Android應用程式介面開發最重要的一個部分。

 

Android系統介面使用者體驗組織(TASKS)

從Android系統介面的組織來看,使用者體驗到的直觀介面都是由很多Tasks組成的。比如,我們從最近工作列,就可以看到很多TASKS。使用者可以隨意的切換到其中的一個TASK。也可以按HOME鍵退出一個TASK。TASK的組織形式是ActivityStack,一個ActivityStack是由好多Activity組成的堆疊。從Android系統設計者的角度來看,一個Task定義了一組行為,而這組行為是由跨多個應用程式的多個Activity組織而構成的。這個打破了以往由應用程式(程序)來定義的資源邊界。一個Task就是一個場景的實現。從此構建系統使用者體驗和行為的邊界在於TASK,而非應用程式或者程序。

 

Task

  • Task 是activities的集合,通過activity stack來管理,依靠先進後出佇列來實現;
  • 每個task中都至少有一個activity,新例項出來的activity置於棧頂
  • Task可以被切換到後臺

Activity Stack  

  • 如上所訴,Activity承擔了大量的顯示和互動工作,從某種角度上將,我們看見的應用程式就是許多個Activity的組合。為了讓這許多 Activity協同工作而不至於產生混亂,Android平臺設計了一種堆疊機制用於管理Activity,其遵循先進後出的原則,系統總是顯示位於棧 頂的Activity,從邏輯上將,位於棧頂的Activity也就是最後開啟的Activity,這也是符合邏輯的。
  • 在操作應用程式時,每次啟動新的Activity,都會將此壓入Activity Stack,當用戶執行返回操作時,移除Activity Stack頂上的Activity,這樣就實現了返回上一個Activty的功能。直到使用者一直返回到Home Screen,這時候可以理解為移除了Activity Stack所有的Activity,這個Activity Stack不再存在,應用程式也結束了執行。
  • 可以通過 adb shell dumpsys |grep ActivityRecord 來檢視 TASKS的ActivityStacks
  • 可以通過 adb shell dumpsys activity activities |grep packageName| grep Run 來檢視某個packageName的ActivityStatcks

 

task的taskAffinity

  • taskAffinity 這個屬性主要是決定持有每個activity屬於哪個task。
  • 預設情況下,同一個包中的activity共享同一個affinity(任務共用性)。

 

task的launchMode

  • standard(default):standard,標準的Activity是可以隨意插入到TASK中去的一個組織結構,可以去TaskA,也可以去TaskB,也可以去TaskC,直接並無任何的聯絡
  • singleTop,如果在任務的棧頂正好存在該Activity的例項,就重用該例項,否則就建立新的例項並放入棧頂。
  • singleTask,如果在棧中已經有該Activity的例項,就重用該例項(會呼叫例項的onNewIntent())。重用時,會讓該例項回到棧頂,因此在它上面的例項將會被移除棧。如果棧中不存在該例項,將會建立新的例項放入棧中。
  • singleInstance

 

Intent Flag介紹

 

  • 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。如果它的啟動模式宣告為“multiple”(預設值),並且你沒有在這個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_CLEAR_WHEN_TASK_RESET    

      如果設定,這將在Task的Activity stack中設定一個還原點,當Task恢復時,需要清理Activity。也就是說,下一次Task帶著FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記進入前臺時(典型的操作是使用者在主畫面重啟它),這個Activity和它之上的都將關閉,以至於使用者不能再返回到它們,但是可以回到之前的Activity。      這在你的程式有分割點的時候很有用。例如,一個e-mail應用程式可能有一個操作是檢視一個附件,需要啟動圖片瀏覽Activity來顯示。這個Activity應該作為e-mail應用程式Task的一部分,因為這是使用者在這個Task中觸發的操作。然而,當用戶離開這個Task,然後從主畫面選擇e-mail app,我們可能希望回到檢視的會話中,但不是檢視圖片附件,因為這讓人困惑。通過在啟動圖片瀏覽時設定這個標誌,瀏覽及其它啟動的Activity在下次使用者返回到mail程式時都將全部清除。

     

     

  • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

      If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

     

     

  • FLAG_ACTIVITY_NEW_TASK     

       如果設定,這個Activity會成為歷史stack中一個新Task的開始。一個Task(從啟動它的Activity到下一個Task中的Activity)定義了使用者可以遷移的Activity原子組。Task可以移動到前臺和後臺;在某個特定Task中的所有Activity總是保持相同的次序。      這個標誌一般用於呈現“啟動”型別的行為:它們提供使用者一系列可以單獨完成的事情,與啟動它們的Activity完全無關。      使用這個標誌,如果正在啟動的Activity的Task已經在執行的話,那麼,新的Activity將不會啟動;代替的,當前Task會簡單的移入前臺。參考FLAG_ACTIVITY_MULTIPLE_TASK標誌,可以禁用這一行為。      這個標誌不能用於呼叫方對已經啟動的Activity請求結果。

     

     

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS    

      如果設定,新的Activity不會在最近啟動的Activity的列表中儲存。

  • 參考一個stackoverflow的問答 https://stackoverflow.com/questions/7759556/flag-activity-exclude-from-recents-excludes-whole-application-not-only-the-acti

  • I have a Notification which starts an Activity. After a long press on home button and selecting my app, I want to start my main Activity again, and not this Activity started by the Notification. I tried with FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, but this removed my whole application from the recents, and that's not what I want to achieve. How can I have my app in the recents, but have the main Activity started?

  • Okay, I found the solution to my problem. I started an Activity from a Notification with FLAG_ACTIVITY_NEW_TASK. But it seems to me that this Activity only gets started in an own task if affinity is different from the default affinity. So I had to add a different affinity in the manifest.

    And it seems that FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS does not (as documented) exlucde the Activity from the recents, rather it excludes the whole task (not the whole application) in which the Activity gets started from the recents. And as I hadn't set a different affinity the Activity which I wanted to exclude was started in the same task (although I had set FLAG_ACTIVITY_NEW_TASK) and so my whole application (as it was running in only one task) was excluded from the recents.

    Now I've set a different affinity for the Activity that gets started from the Notification and I start it with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. When I leave this Activity and long-press the HOME button I can choose my app and the default task is started or brought to the front.

     

     

  • FLAG_ACTIVITY_FORWARD_RESULT     

     

      如果設定,並且這個Intent用於從一個存在的Activity啟動一個新的Activity,那麼,這個作為答覆目標的Activity將會傳到這個新的Activity中。這種方式下,新的Activity可以呼叫setResult(int),並且這個結果值將傳送給那個作為答覆目標的Activity。