Activity 知識梳理(2) Activity 棧

澤毛發表於2017-12-21

一、AndroidManifest.xml中指定launchMode

1.1standard

標準模式,每次啟動Activity都會建立一個新的Activity例項,並且將其壓入任務棧棧頂,而不管這個 Activity 是否已經存在,都會執行onCreate() ->onStart() -> onResume

1.2 singleTop

棧頂複用模式,如果新Activity已經位於棧頂,那麼此Activity不會被重新建立,同時ActivityonNewIntent方法會被回撥,如果Activity已經存在但是不再棧頂,那麼和standard模式一樣。 如果Activity當前是onResume狀態,那麼呼叫後會執行onPause() -> onNewIntent() -> onResume()

1.3 singleTask

棧內複用模式,建立這樣的Activity,系統會確認它所需任務棧是否已經建立,否則先建立任務棧,然後放入Activity如果棧中已經有一個Activity例項,那麼會做兩件事:

  • 這個Activity會回到棧頂執行onNewIntent
  • 清理在當前Activity上面的所有Activity

上面的如果棧中已經有一個Activity例項,這個判斷條件的**標準是由android:taskAffinity**決定的,下面我們做一個簡單的對比:

  • 第一種情況,不給singleTaskActivity設定taskAffinity,這時預設情況下屬於同一個Application的所有Activity具有的taskAffinity是相同的,就是我們在AndroidManifest中指定的packageName
<activity android:name=".SingleTaskActivity" android:launchMode="singleTask"/>
複製程式碼

這時候我們從MainActivity啟動SingleTaskActivity後,任務棧的情況是,MainActivitySingleTaskActivity處於同一個Task當中:

Activity 知識梳理(2)   Activity 棧
我們看到SingleTaskActivityMainActivity位於同一個棧中,因此singleTask並不是讓這個Activity獨佔一個Task

  • 第二種情況,給singleTaskActivity設定affinity
<activity android:name=".SingleTaskActivity" android:launchMode="singleTask" android:taskAffinity="com.android.singleTask"/>
複製程式碼

此時進行同樣的操作,任務棧的情況變為:

Activity 知識梳理(2)   Activity 棧
我們在SingleTaskActivity的介面按下 Home鍵,再點選圖示進入MainActivity,可以看到當前應用有兩個棧:

Activity 知識梳理(2)   Activity 棧
此時,我們再點選按鈕啟動SingleTaskActivity,那麼會執行

MainActivity#onPause
SingleTaskActivity#onNewIntent 
SingleTaskActivity#onRestart 
SingleTaskActivity#onStart 
SingleTaskActivity#onResume
MainActivity#onStop
複製程式碼

這是由於當啟動SingleTaskActivity,系統去尋找該SingleTaskActivity所對應的棧是否存在,而這時候是存在的,也就上面看到TaskRecord[cd9ca5d],所以它不會建立新的SingleTaskActivity,而是複用這個棧中的Activity,而由於這個Activity又位於棧頂,因此它的表現和SingleTop相同。

  • 第三種情況,我們試著在第二種的基礎上再加大一些難度, 在SingleTaskActivity所在的Task上再加一個 SingleTaskAboveActivity,首先我們從MainAcitivity -> SingleTaskActivity -> SingleTaskAboveActvity
<activity android:name=".SingleTaskAboveActivity"/>
複製程式碼

這一流程過後,棧的結構為,可以看到SingleTaskActivitySingleTaskAboveActvity位於同一棧中:

Activity 知識梳理(2)   Activity 棧
SingleTaskAboveActvity介面,按Home退到後臺之後重新進入,棧的結構不變,只不過當前可見的是 MainActivity,這時我們再次嘗試啟動SingleTaskActivity,那麼會依次呼叫:

MainActivity#onPause
SingleTaskAboveActivity#onDestroy
SingleTaskActivity#onNewIntent
SingleTaskActivity#onRestart
SingleTaskActivity#onStart
SingleTaskActivity#onResume
MainActivity#onStop
複製程式碼

而棧的結構變為如下:

Activity 知識梳理(2)   Activity 棧
和第二種情況類似,當啟動SingleTaskActivity時,系統去尋找該SingleTaskActivity所對應的棧是否存在,而這時候是存在的,也就上面看到TaskRecord[a3771ca],所以它不會建立新的SingleTaskActivity,而是複用這個棧中的SingleTaskActivity,但此時SingleTaskActivity並不位於棧頂,在它上面還有一個SingleTaskAboveActivity,因此會把SingleTaskAboveActivity先出棧,再複用原先位於這個棧中的SingleTaskActivity例項。

1.4 singleInstance

這種模式的Activity只能單獨位於一個任務棧內,由於棧內的複用特性,後續請求均不會建立新的Activity,除非這個獨特的任務棧被系統銷燬了。

  • 第一種情況,先看最簡單的,我們新建一個SingleInstanceActivity
<activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance"/>
複製程式碼

我們從MainAcitivity啟動它,此時任務棧的情況是,他們位於不同的Task中,符合我們的預期:

Activity 知識梳理(2)   Activity 棧

  • 第二種情況,此時按Home回到桌面,再重新點圖示進入MainActivity,任務棧依然是兩個,我們此時再啟動SingleInstanceActivity
MainActivity#onPause
SingleInstanceActivity#onNewIntent 
SingleInstanceActivity#onRestart 
SingleInstanceActivity#onStart 
SingleInstanceActivity#onResume
MainActivity#onStop
複製程式碼

和啟動在另一個棧中已存在的singleTaskAcitivity的情況是類似的。

  • 第三種情況,我們再看一下,affinity對於singleInstance會不會有影響呢,我們定義兩個affinity相同的 singleInstance
 <activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance" android:taskAffinity="com.android.singleInstance"/>
<activity android:name=".SingleInstanceActivityAnother" android:launchMode="singleInstance" android:taskAffinity="com.android.singleInstance"/>
複製程式碼

我們先從MainActivity啟動SingleInstanceActivity,按Home回到桌面再進入,此時Task的情況和上面相同的,那麼這時候我們啟動SingleInstanceActivityAnother

singleInstance_another.png
可以看到,它並沒有受到affinity的影響,而是重新起了一個新的棧。

二、在Intent當中指定啟動模式

2.1 FLAG_ACTIVITY_NEW_TASK

singleTask行為相同,前面已經詳細分析過了,這裡需要注意 affinity 的宣告。

2.2 FLAG_ACTIVITY_SINGLE_TOP

singleTop行為相同,比較簡單,就不舉例子了。

2.3 FLAG_ACTIVITY_CLEAR_TASK

FLAG_ACTIVITY_NEW_TASK 何用,這個Activity會新起一個棧,原來棧被清空,棧中的Activity也被銷燬。

2.5 FLAG_ACTIVITY_CLEAR_TOP

會清除這個Activity之上所有的Activity,我們來試一下,新建兩個新的SecondActivityThirdActivity,從Main -> Second -> Third,此時棧的結構是:

clearTop_1.png
此時,我們按如下方式啟動SecondActivity

    public void third(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
    }
複製程式碼

這之後,棧的結構變為,ThirdAcitivity被出棧了:

clearTop_2.png

2.6 FLAG_ACTIVITY_REORDER_TO_FRONT

上面的FLAG_ACTIVITY_CLEAR_TOP是把位於目標Activity之上的Activity都銷燬,而則個FLAG則是對棧重新排序,把目標Activity移到最前臺,其它的位置不變,我們在前一種的基礎上,在ThirdActivity中換一種方式來啟動SecondActivity

    public void third(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivity(intent);
    }
複製程式碼

這回,最終棧的結構變為了,可以看到ThirdActivity並沒有被出棧:

Paste_Image.png

三、AndroidManifest中的屬性

3.1 alwaysRetainTaskState

這個標誌只對根Activity有用,預設情況下,當我們的應用在後臺一段時間,它會銷燬該Task除了根以外的所有Activity,如果我們希望保持這個Task的原有狀態,那麼給這個Task的根Activity設定這個屬性,預設值是false

3.2 clearTaskOnLaunch

從桌面啟動該Activity的時候會清空該Task除了根Activity外的所有Activity,我們從Main -> Second -> Third,此時棧內有3個Activity,按Home回到桌面後,點圖示重新進入,此時Task只剩下根Activity 了:

Paste_Image.png

3.3 finishOnTaskLaunch

這個和上面類似,但是它對根Activity無效,我們給SecondActivity設定這個屬性,先啟動到ThirdActivity,這時候棧的結構為:

Paste_Image.png

接著,我們按Home回到桌面,點圖示重新進入,棧的結構變為下面這樣,可以看到SecondActivity沒有了:

Paste_Image.png

3.4 noHistory

Activity在不可見之後,不儲存記錄

  • 第一種情況,我們給SecondActivity設定這個屬性,接著從Main -> Second -> Third,然後按Back返回,此時的生命週期為:
ThirdActivity#onPause
MainActivity#onRestart
MainActivity#onStart
MainActivity#onResume
SecondActivity#onDestroy
ThirdActivity#onStop
ThirdActivity#onDestroy
複製程式碼
  • 第二種情況,如果我們在ThirdActivity時,不是按Back,而是按Home到桌面,會呼叫:
ThirdActivity#onPause
SecondActivity#onDestroy
ThirdActivity#onStop
複製程式碼
  • 第三種情況,我們給根MainAcitivity設定這個屬性,啟動它後退出:
MainActivity#onCreate
MainActivity#onStart
MainActivity#onResume
MainActivity#onDestory
複製程式碼

相關文章