任務和回退棧

任務和回退棧

  一個應用通常有多個Activity。每個Activity圍繞着用戶要執行的具體行爲被設計,其可以啓動其他的Activity。例如,郵件應用有一個Activity用來展示新消息列表。當用戶點擊一個消息項,打開一個新Activity來查看該條消息詳情。
  一個Activity也能啓動一個設備上的其他應用的Activity。例如,如果你的應用想要發一封郵件,則你可以定義一個執行”發送”行爲且包含需要數據(比如郵件地址和消息體)的Intent。其他應用程序聲明瞭處理這種的類型Intent的Activity將被打開。上面的情況,定義的Intent是要發送郵件,所以郵件應用的“撰寫”Activity啓動(如果有多個Activity支持這一相同的Intent,則系統會讓用戶選擇使用哪一個)。當郵件被髮送,你的Activity將恢復,這看起來好像郵件Activity是你的應用的一部分。儘管activity來自不同的應用,Android通過保持所有的Activity在一個相同的任務棧來維持無縫的用戶體驗。
  任務是當執行一項具體工作時用戶與之交互的Activity的集合。這些Activity按打開的順序被放置在一個棧裏(回退棧)。
  設備的主屏幕是大部分任務啓動的地方。當用戶在桌面上點擊一個應用程序圖標時,這個應用程序的任務被調到前臺。如果這個應用程序沒有任務存在,則會創建一個新的任務,並且該應用的“主”Activity將被打開作爲該任務棧的根Activity。
  噹噹前的Activity啓動另一個Activity時,這個新的Activity被壓入到任務棧的棧頂。前一個Activity仍在任務棧裏,只是被標記爲停止狀態。當一個Actvity停止,系統保持着用戶交互的當前狀態。當點擊返回鍵時,當前的Activity從任務棧的棧頂彈出(該Activity被銷燬),之前的Activity恢復。Activity絕不會在任務棧中重新排列,僅會將啓動的Activity壓入棧中,將點擊返回鍵後的Activity彈出棧中。下圖展示了Activity在時間進程上的行爲:
這裏寫圖片描述

  如果用戶持續點擊返回鍵,則每次在棧頂的Activity會被彈出且展示之前的Activity,直到返回到主界面。當所有的Activity都從任務棧中移除,該任務不再存在。
  當用戶開啓一個新的任務或通過Home鍵回到主屏幕時,任務作爲一個整體的單元被移動到後臺。當處於後臺,該任務棧中所有Activity被停止,但任務棧仍在,其只是簡單地失去了焦點。如下圖所示:
這裏寫圖片描述

  當用戶重新拉起應用,則其任務會重新回到前臺。
  注意後臺可以同時保持多個任務。但是,如果用戶在後臺同時運行着多個任務,則系統可能會銷燬後臺的Activity以回收內存,這會是這些Activity的狀態丟失(關於更多狀態保持與恢復的內容,請查看Android狀態保存與恢復)。
  因爲在任務棧中的Activity絕不會重新排列,所以如果應用運行用戶啓動一種具體的Activity多餘一個,則這種Activity的新的實例將被創建並壓入任務棧中,而不是將之前的該Activity的實例移到棧頂。像這樣,你的應用中的一個Activity可能會被實例化多次,甚至是在不同的任務棧中。如下圖所示:
這裏寫圖片描述

  然而,如果你不想一個Activity被實例化多次,則可以修改這種行爲。這將在後面的“管理任務棧”一節講到。
  下面總結一下Activity和任務的默認行爲:

  • 當Activity A啓動Activity B,Activity A停止,但系統仍會保持它的狀態。如果用戶在Activity B點擊了返回鍵,則Activity A從保存的狀態中恢復。
  • 當用戶通過點擊Home鍵離開任務,當前Activity停止且它的任務棧調度到後臺。系統保持了任務棧中每一個Activity的狀態。如果隨後用戶點擊了桌面圖標,則該任務會被調度到前臺並且恢復棧頂的Activity。
  • 如果用戶點擊了返回鍵,當前Activity將從任務棧中彈出並被銷燬。在棧中的之前的Activity會被恢復。
  • Activity可以被實例化多次,且可以在不同任務棧中。

管理任務棧

  同上面的描述,Android管理任務和回退棧的方式是將連續啓動的Activity放置在同一任務棧中,對於大多數的應用,這可以工作的很好,你不需要擔心你的Activity和任務之間是怎樣關聯的或它們是怎樣存在於任務棧中的。然而,你可以決定是否打斷這種正常的行爲。也許你想你的應用中的一個Activity在啓動時創建一個新的任務,或是,當啓動一個Activity時,你想將一個已經存在的實例移到棧頂,再或是,你想在離開任務時,將任務棧中除root Activity中的所有activity清除。
  你可以使用manifest中的<activity>元素的屬性或傳遞到startActivity()的Intent中的flags,來實現這些需求。
  
與其相關的,主要的<activity>屬性如下:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

與其相關的主要的Intent flags有:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

在下一節中,將給出怎樣使用<activity>屬性和Intent flags來定義activity和任務之間的關係與它們在回退棧中的行爲表現。

  注意,大部分應用不應該打斷Activity和任務之間的默認行爲。如果你認爲修改默認行爲是必要的,那一定要測試該Activity的使用情況是否如你所願,以確保其不會打斷用戶期待的行爲。

定義啓動模式

  啓動模式允許你定義一個Activity的新實例怎樣與當前任務關聯。你可以用下面兩種方式定義啓動模式:

使用manifest文件

  當你在manifest文件中聲明Activity時,你可以定義其啓動時與任務的關聯方式。
  具體可以使用<activity>元素的lauchMode屬性來定義該Activity應該怎樣在任務中創建。有四種不同啓動模式可以設置給lauchMode屬性。

  • standard
    默認設置。每次啓動該種Activity時,系統在任務中創建一個新的Activity實例。這種Activity能夠被實例化多次,每個實例屬於不同的任務,每個任務可以有多個實例。
  • singleTop
    如果該Activity的一個實例存在與當前任務棧的棧頂,系統將啓動Intent通過onNewIntent()傳遞給該實例,而不會新建實例。如果已有該實例不在棧頂,則會新建實例。該Activity會被實例化多次,每個實例可以屬於不同的任務,每個任務可以有多個實例。
    注意,當該Activity的一個新實例被創建,用戶可以點擊返回鍵回到之前的Activity。但當一個Activity的已存在實例處理了這個啓動Intent時,用戶點擊返回鍵不能回到調用onNewIntent()進行處理之前的狀態。
  • singleTask
    該種啓動模式的Activity僅會有一個實例。如果已有任務棧中存在該Activity實例,則系統會將啓動它的Intent通過調用其onNewIntent()方法傳遞給它,而不會再創建一個新的實例,且該Activity上面的所有Activity將以合適的方式自動銷燬。如果還沒有該Activity實例,則其表現受taskAffinity屬性影響,如果taskAffinity屬性標識的任務不存在,則會新建一個任務,並將該Activity作爲根Activity,如果taskAffinity屬性標識的任務存在,則將該Activity壓入該任務棧。
    注意,儘管Activity會被啓動在一個新的任務中,點擊返回鍵仍然能返回到之前的Activity。
  • singleInstance
    除了系統不會再將其他Activity放入該Activity所在任務,其他同singleTask一樣。即該Activity是其所在任務的唯一成員,任何由它啓動的Activity將被放置在分隔的任務棧中。

不管一個Activity是否啓動在一個新的任務中還是在與啓動它的Activity相同的任務棧中,點擊返回鍵時,都會回到前一個Activity。然而,如果你啓動的是singleTask模式的Activity,且如果其實例存在於後臺,則整個任務會被帶到前臺。這時,回退棧將包含所有從後臺帶到前臺的Activity,並且被放置在棧頂。如下圖所示:
這裏寫圖片描述

使用Intent flag

  當你調用startActivity()時,你可以在Intent中設置flag來聲明啓動的Activity與任務的關聯方式。
  像這樣,如果Activity A啓動了Activity B,Activity B能在manifest中定義它應該與當前任務棧怎樣關聯,並且Activity A也可以請求Activity B怎樣與當前任務關聯。如果兩個全部定義了,則Activity A的請求是優於Activity B的請求。
  可以用來修改默認行爲的flag有:

  • FLAG_ACTIVITY_NEW_TASK
    其行爲同launchMode設置爲“singleTask”。
  • FLAG_ACTIVITY_SINGLE_TOP
    其行爲同launchMode設置爲“singleTop”。
  • FLAG_ACTIVITY_CLEAR_TOP
    如果被啓動的Activity已經存在於當前任務中,則其上的所有Activity都被銷燬,啓動Intent傳遞到onNewIntent()方法,來恢復該Activity。
    沒有對應的launchMode屬性。

FLAG_ACTIVITY_CLEAR_TOP經常和FLAG_ACTIVITY_NEW_TASK一起使用。當它們一起使用時,這些flags是將一個已經存在的Activity放置到另一個任務中,並將其放在一個能響應該Intent的位置上。
注意,如果被指定的Activity的啓動模式是“standard”,則它將從任務棧中移除,並在它的位置處創建一個新的實例來處理這個Intent。這是因爲,當啓動模式是“standard”時,對於每一個新的Intent都會創建一個新的實例。

  注意,manifest中可獲取的啓動模式不一定在Intent的flag中可獲取,同樣的,Intent的flag中可獲取的啓動模式在manifest中不一定可以定義。

處理關係

  “關係”指示一個Activity屬於哪個任務。默認的,所有來自同一個應用程序的Activity有相同的“關係”。所以默認情況下,同一應用程序的所有Activity屬於統一任務。但是,你可以修改一個Activity的關係。在不同應用程序的Activity可以共享同一個“關係”,同一應用程序的的Activity可以分配不同的“關係”。
可以使用<activity>元素的taskAffinity屬性來修改給定Activity的“關係”。
taskAffinity屬性的值是一個字符串。系統使用包名來標識一個應用程序的默認任務“關係”。爲一個activity的taskAffinity設置一個空字符串,表明這個activity不屬於任何task。
  “關係”用於下面兩種情況:

  • 當創建Activity的Intent包含FLAG_ACTIVITY_NEW_TASK標籤時。
    默認情況下,一個新的Activity被創建在調用startActivity()方法的Activity所在任務中。它被壓入調用者所在的回退棧中。然而,如果傳遞到startActivity()方法中的Intent的包含FLAG_ACTIVITY_NEW_TASK標籤,則系統會根據條件找尋一個合適的任務來放置這個新的Activity。通常,它是一個新的任務。但是,不總是。如果已經存在一個與這個新的Activity標識的“關係”相同的任務,則這個Activity被創建到這個任務中。否則,啓動一個新任務。
    有些實體(比如通知欄管理器)會一直在一個外部任務中啓動Activity,所以它們一直在傳遞到startActivity()方法中的Intent中設置FLAG_ACTIVITY_NEW_TASK標籤。如果你有一個Activity被外部使用啓動,則可能使用了這個標籤,注意這是用戶有獨立的方式返回到啓動它的任務,比如點擊桌面圖標。

  • 當Activity的allowTaskReparenting屬性設置爲true時。
    這種情況下,當該Activity標識的“關係”的任務返回到前臺時,該Activity可以從啓動它的任務移動到同其“關係”的任務中。
    例如,假設一個報導天氣條件的Activity被定義爲一個旅遊應用的一部分,且其具有其所在應用程序的其他Activity相同的“關係”(應用程序的默認關係)並允許使用allowTaskReparenting屬性設置允許re-pareting。當你的Activity之一啓動了這個天氣預報Activity時,它初始屬於你的Activity的任務。但是,當這個旅遊應用的任務返回前臺時,這個天氣預報Activity被重新安排到旅遊應用的任務中並展示它。

清理回退棧

  如果用戶長時間離開一個任務,則系統會清理掉任務中除根Activity之外的所有Activity。但用戶再次回到任務中,僅根Activity是被恢復。系統表現這種行爲的原因是,用戶離開很久後,可能已經放棄了他們之前正在進行的操作,所以返回到任務開始一個新的操作。
  有很多的Activity屬性可以用來修改這個行爲:

  • alwaysRetainTaskState
    如果任務中的根Activity的這個屬性被設置爲true,則上面被描述的默認行爲不會發生。很長一段時間後,該任務仍保持所有Activity。
  • clearTaskOnLaunch
    如果任務中的根Activity的這個屬性被設置爲true,則當用戶離開任務並返回時,該任務棧被清理直到根Activity。用戶會一直以初始狀態返回任務,甚至只是離開一小會。
  • finishOnTaskLaunch
    這個屬性很像 clearTaskOnLaunch,但它操作在一個單獨的Activity上,而不是整個任務上。它會導致任何Activity銷燬,包括根Activity。當它被設置爲true,該Activity僅存在當前會話任務中。如果用戶離開後返回該任務,則它不在存在。

啓動任務

  可以通過給一個Activity設置”android.intent.action.MAIN”和”android.intent.category.LAUNCHER”Intent 過濾器來將其設置爲一個任務的入口。例如:

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>

  這種類型的Intent過濾器會爲該Activity產生一個圖標和標籤展示在應用程序啓動器中,以提供給用戶一個方式去創建該Activity或在它創建後可以任意次回到其創建的任務。
  其第二個能力是重要的:用戶必須能夠在離開任務後,又能使用該Activity啓動器回到它。因爲這個原因,作爲啓動器的Activity應該僅在該Activity有ACTION_MAIN 和CATEGORY_LAUNCHER過濾器時,才使用singleTasksingleInstance這兩個啓動模式。例如,如果這兩個過濾器沒有添加,一個Intent創建了一個singleTaskActivity,初始化了一個任務,且用戶在該任務上執行了很多操作,這時用戶點擊Home鍵。這個任務被放到後臺不可見,這時用戶沒有了返回到這個任務的方法。這是因爲,它不會顯示在應用程序啓動器中。

回退任務

  在啓動一系列Activity後,可能會創建幾個任務,其中每個任務在回退棧中作爲一項,在用戶點擊返回鍵時,回退棧的默認回退情況(爲對返回鍵處理重寫等)是,先獲得回退棧中棧頂的任務,然後彈出該任務棧棧頂的Activity實例,當該任務棧中的所有Activity實例都被彈出,則該任務從回退棧中彈出,並銷燬,執行這個回退過程,直到回退到桌面。

四種啓動模式的關鍵結論驗證

launchMode:standard

  standard是默認的啓動模式,所以Activity不設置launchMode,則其啓動模式爲standard

被啓動Activity和啓動Activity的taskAffinity相同

FirstActivity註冊
 <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity android:name=".SecondActivity" />

驗證1:該種Activity不啓動新的任務,被啓動的Activity被壓入啓動它的Activity所在的任務棧中

  執行FirstActivity啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
TaskRecord{27db6384 #3208 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity (has extras) }
        Hist #1: ActivityRecord{395c8c83 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3208}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{cd3c76d 16254:com.example.sunxiaodong.taskandbackstack/u0a379}
        Hist #0: ActivityRecord{3240b0d0 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3208}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity (has extras) }
          ProcessRecord{cd3c76d 16254:com.example.sunxiaodong.taskandbackstack/u0a379}

  從任務棧情況可以得到,SecondActivity被壓入FirstActivity所在的任務棧中。

驗證2:每次啓動該種Activity,系統會在任務中創建一個新的Activity實例

  執行FirstActivity啓動SecondActivity,SecondActivity中再次啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
TaskRecord{2ab63d02 #3211 A=com.example.sunxiaodong.taskandbackstack U=0 sz=3}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #2: ActivityRecord{3c598601 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3211}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{18925050 13122:com.example.sunxiaodong.taskandbackstack/u0a379}
        Hist #1: ActivityRecord{2d1693 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3211}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{18925050 13122:com.example.sunxiaodong.taskandbackstack/u0a379}
        Hist #0: ActivityRecord{ddb9d9 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3211}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{18925050 13122:com.example.sunxiaodong.taskandbackstack/u0a379}

  從任務棧情況可以得到,任務棧中已存在SecondActivity實例的情況下,再啓動SecondActivity還會創建一個新的SecondActivity實例壓入棧頂。

被啓動Activity和啓動Activity的taskAffinity不同

驗證1:如果被啓動Activity的taskAffinity所標識的任務不存在,則該種Activity不能創建新任務

FirstActivity註冊
 <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.second" />

  執行FirstActivity啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
TaskRecord{1abd4247 #3369 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #1: ActivityRecord{17e1a735 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3369}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{1c02e7c3 18114:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{3d5eef14 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3369}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{1c02e7c3 18114:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧情況得到,standard啓動模式的Activity不會創建taskAffinity標識的新的任務。

驗證2:如果被啓動Activity的taskAffinity所標識的任務已存在,則被啓動Activity的實例會放入啓動其Activity所在任務棧中,而不管啓動Activity是否在被啓動Activity的taskAffinity標識任務中

FirstActivity註冊
 <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.second" />
ThirdActivity註冊
<activity android:name=".ThirdActivity" />

  執行FirstActivity啓動SecondActivity,SecondActivity啓動ThirdActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3374
      TaskRecord{1ac34ecf #3374 A=com.example.sunxiaodong.taskandbackstack.second U=0 sz=2}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #1: ActivityRecord{18fa9f27 u0 com.example.sunxiaodong.taskandbackstack/.ThirdActivity t3374}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
          ProcessRecord{3c0de43a 29526:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{43cba37 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3374}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{3c0de43a 29526:com.example.sunxiaodong.taskandbackstack/u0a387}
    Task id #3372
      TaskRecord{2a45d05c #3372 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #0: ActivityRecord{253e4bf4 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3372}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{3c0de43a 29526:com.example.sunxiaodong.taskandbackstack/u0a387}

  任務棧情況,符合驗證的結論。

launchMode:singleTop

FirstActivity註冊
 <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:launchMode="singleTop" />

驗證1:被啓動Activity在啓動其Activity所在任務棧的棧頂,則不會創建被啓動Activity的新實例

  執行FirstActivity啓動SecondActivity,SecondActivity再啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
TaskRecord{35412fbd #3376 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #1: ActivityRecord{1fbbf35c u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3376}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{11ea5d03 30105:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{3456a7b u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3376}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{11ea5d03 30105:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧情況得到,啓動模式爲singleTop的被啓動Activity在啓動其Activity所在任務棧的棧頂時,不會創建新實例。

驗證2:被啓動Activity不在啓動其Activity所在任務棧的棧頂,則會在啓動Activity所在任務棧的棧頂創建新的被啓動Activity實例

ThirdActivity註冊
<activity
            android:name=".ThirdActivity"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.third" />

  執行FirstActivity啓動SecondActivity,SecondActivity啓動ThirdActivity,ThirdActivity再啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3381
      TaskRecord{20eb4d89 #3381 A=com.example.sunxiaodong.taskandbackstack.third U=0 sz=2}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
        Hist #1: ActivityRecord{1fdcca07 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3381}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{163795bc 11943:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{1cdc6d17 u0 com.example.sunxiaodong.taskandbackstack/.ThirdActivity t3381}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
          ProcessRecord{163795bc 11943:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3379
      TaskRecord{36c5f18e #3379 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #1: ActivityRecord{3fc54f79 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3379}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{163795bc 11943:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{104fbd9a u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3379}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{163795bc 11943:com.example.sunxiaodong.taskandbackstack/u0a387}

  啓動模式爲singleTop的其他啓動情況與啓動模式爲standard的情況相同,不再給出驗證。

launchMode:singleTask

驗證1:如果被啓動Activity的taskAffinity屬性標識的任務爲啓動Activity所在任務,則不會新建任務

FirstActivity註冊
 <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:launchMode="singleTask" />

  執行FirstActivity啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
TaskRecord{220c4e08 #3406 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #1: ActivityRecord{2606f228 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3406}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{1ced3387 30484:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{27f048cb u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3406}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{1ced3387 30484:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧情況可以得到,啓動模式爲singleTask的Activity並不是每次啓動都會新建任務,當其taskAffinity屬性所標識的任務爲啓動Activity所在任務時,不會新建任務,即其taskAffinity屬性所標識的任務已存在時,不會創建新的任務。

驗證2:如果被啓動Activity的taskAffinity屬性標識的任務不存在,則會新建任務

SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.second" />

  執行FirstActivity啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3410
      TaskRecord{3298bcfc #3410 A=com.example.sunxiaodong.taskandbackstack.second U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #0: ActivityRecord{1c628411 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3410}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{1ed6d60b 23610:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3409
      TaskRecord{3b856f85 #3409 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #0: ActivityRecord{29642a8a u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3409}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{1ed6d60b 23610:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧情況可以得到,如果被啓動Activity的taskAffinity屬性標識的任務不存在,則會新建任務。

驗證3:如果被啓動Activity在棧中上面還有其他Activity實例,則這些Activity將以合適的方式自動銷燬

FirstActivity註冊
<activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>
SecondActivity註冊
<activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.second" />
ThirdActivity註冊
<activity android:name=".ThirdActivity" />
FourthActivity註冊
<activity
            android:name=".FourthActivity"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.fourth" />

  執行FirstActivity啓動SecondActivity,SecondActivity啓動ThirdActivity,ThirdActivity啓動FourthActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3416
      TaskRecord{36ee9249 #3416 A=com.example.sunxiaodong.taskandbackstack.fourth U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FourthActivity }
        Hist #0: ActivityRecord{252b608a u0 com.example.sunxiaodong.taskandbackstack/.FourthActivity t3416}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FourthActivity }
          ProcessRecord{22850a05 3430:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3415
      TaskRecord{19cb74e #3415 A=com.example.sunxiaodong.taskandbackstack.second U=0 sz=2}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #1: ActivityRecord{18f7b294 u0 com.example.sunxiaodong.taskandbackstack/.ThirdActivity t3415}
          Intent { cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
          ProcessRecord{22850a05 3430:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{1d6f71ff u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3415}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{22850a05 3430:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3413
      TaskRecord{3e8006f #3413 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #0: ActivityRecord{1648bf23 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3413}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{22850a05 3430:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧情況可知,當前有3個任務,其中SecondActivity和ThirdActivity 在一個任務中,且ThirdActivity的實例在SecondActivity的實例之上。

  接着上面,執行FourthActivity啓動SecondActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3419
      TaskRecord{20698cd0 #3419 A=com.example.sunxiaodong.taskandbackstack.second U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #0: ActivityRecord{28bfef87 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3419}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{722a5fc 10546:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3421
      TaskRecord{2eb884c9 #3421 A=com.example.sunxiaodong.taskandbackstack.fourth U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FourthActivity }
        Hist #0: ActivityRecord{cd70e2d u0 com.example.sunxiaodong.taskandbackstack/.FourthActivity t3421}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FourthActivity }
          ProcessRecord{722a5fc 10546:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3418
      TaskRecord{197bf7ce #3418 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #0: ActivityRecord{270d8dc9 u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3418}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{722a5fc 10546:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧的情況可知,SecondActivity啓動後,其所在任務中的ThirdActivity銷燬了,SecondActivity被放置在了棧頂。

launchMode:singleInstance

FirstActivity註冊
<activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>

驗證1:該種Activity的任務中,只會有一個實例,且由該種Activity啓動Activity,如果被啓動Activity的taskAffinity屬性標識的任務存在,則將實例壓入該任務中,如果不存在,則新建該任務

ThirdActivity註冊
<activity android:name=".ThirdActivity" />

  執行FirstActivity啓動SecondActivity,SecondActivity啓動ThirdActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3423
      TaskRecord{193241ab #3423 A=com.example.sunxiaodong.taskandbackstack U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #1: ActivityRecord{1b89f8e8 u0 com.example.sunxiaodong.taskandbackstack/.ThirdActivity t3423}
          Intent { flg=0x10400000 cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
          ProcessRecord{92fe3a1 13471:com.example.sunxiaodong.taskandbackstack/u0a387}
        Hist #0: ActivityRecord{26f251dd u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3423}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{92fe3a1 13471:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3425
      TaskRecord{304529c6 #3425 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #0: ActivityRecord{2f0cb55d u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3425}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{92fe3a1 13471:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧中情況可知,SecondActivity啓動的ThirdActivity的taskAffinity屬性標識的任務存在,則不再新建任務,而是將其壓入已存在的任務中。

ThirdActivity註冊
<activity
            android:name=".ThirdActivity"
            android:taskAffinity="com.example.sunxiaodong.taskandbackstack.third" />

  給上面的ThirdActivity設置android:taskAffinity屬性後,執行FirstActivity啓動SecondActivity,SecondActivity啓動ThirdActivity。

任務棧情況(執行adb shell dumpsys activity
Task id #3430
      TaskRecord{666e0ca #3430 A=com.example.sunxiaodong.taskandbackstack.third U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
        Hist #0: ActivityRecord{a8a6e49 u0 com.example.sunxiaodong.taskandbackstack/.ThirdActivity t3430}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.ThirdActivity }
          ProcessRecord{3d776eb1 21693:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3429
      TaskRecord{3887723b #3429 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
        Hist #0: ActivityRecord{d159cd9 u0 com.example.sunxiaodong.taskandbackstack/.SecondActivity t3429}
          Intent { flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.SecondActivity }
          ProcessRecord{3d776eb1 21693:com.example.sunxiaodong.taskandbackstack/u0a387}
Task id #3427
      TaskRecord{1d7a3f96 #3427 A=com.example.sunxiaodong.taskandbackstack U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
        Hist #0: ActivityRecord{2cc6f9c u0 com.example.sunxiaodong.taskandbackstack/.FirstActivity t3427}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.sunxiaodong.taskandbackstack/.FirstActivity }
          ProcessRecord{3d776eb1 21693:com.example.sunxiaodong.taskandbackstack/u0a387}

  從任務棧中情況可知,SecondActivity啓動的ThirdActivity的taskAffinity屬性標識的任務不存在,則會新建任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章