對(duì)于Android的啟動(dòng)模式,似乎是最簡(jiǎn)單的入門知識(shí)了,
Standard、SingleTop、SingleTask、SingleInstance。
本文重點(diǎn)對(duì)singleTask進(jìn)行加深理解。
入門及使用
任務(wù)棧(Task)
要了解啟動(dòng)模式的具體表現(xiàn)流程,首先要了解任務(wù)棧。官網(wǎng)介紹說任務(wù)與回退棧,這里直接成為任務(wù)棧。
一個(gè)Application一般是由多個(gè)Activity構(gòu)成,而多個(gè)Activity的管理,Android已經(jīng)幫我們管理好了,使用棧方式管理,也就是先進(jìn)后出,啟動(dòng)模式可以理解為四種不同的進(jìn)棧出棧方式。而這個(gè)管理activity的棧成為任務(wù)棧。
- 一個(gè)app默認(rèn)只有一個(gè)任務(wù)棧,由系統(tǒng)指定。
- 一個(gè)app可以存在多個(gè)任務(wù)棧,需要自己手動(dòng)設(shè)置。多個(gè)任務(wù)棧時(shí),當(dāng)前
activity所在棧處于前臺(tái),其余棧處于后臺(tái)。
啟動(dòng)模式
1. standard
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".A"
android:launchMode="standard"/>
standard是android中默認(rèn)的啟動(dòng)模式,如果你新建一個(gè)應(yīng)用,不進(jìn)行任何修改,那么MainActivity的啟動(dòng)模式就是standard。
standard會(huì)進(jìn)行重復(fù)創(chuàng)建實(shí)例,比如應(yīng)用中有Main和A兩個(gè)activity,那么Main -> A -> A -> Main這樣的啟動(dòng)順序,就會(huì)如下圖所示:

點(diǎn)擊back返回,則會(huì)正常出棧,直到最后一個(gè)出棧,app關(guān)閉回到桌面。

這種模式一般不推薦使用,會(huì)占用過多內(nèi)存,頁面重復(fù)。
2. singleTop 棧頂復(fù)用
<activity android:name=".A"
android:launchMode="singleTop"/>
只有這個(gè)activity處于棧頂位置時(shí),不會(huì)重新創(chuàng)建實(shí)例,而是會(huì)使用已經(jīng)創(chuàng)建好的實(shí)例。并且會(huì)調(diào)用onNewIntent方法,可以根據(jù)此方法獲取intent實(shí)例。

應(yīng)用情景:
點(diǎn)擊通知跳轉(zhuǎn)到文章詳情頁,如果當(dāng)前用戶正在文章詳情頁查看時(shí)進(jìn)行點(diǎn)擊,那么可以使用singleTop來確保不會(huì)重復(fù)創(chuàng)建實(shí)例,避免用戶點(diǎn)擊回退鍵時(shí)還是文章詳情頁。
3. singleTask 棧內(nèi)只有一個(gè)實(shí)例
<activity android:name=".Main"
android:launchMode="singleTask"/>
singleTask模式是棧內(nèi)只允許存在一個(gè)實(shí)例,當(dāng)這個(gè)實(shí)例已經(jīng)創(chuàng)建,會(huì)直接使用這個(gè)實(shí)例,而不會(huì)重新創(chuàng)建,在這個(gè)實(shí)例上方的其他activity也將全部出棧銷毀。重用實(shí)例會(huì)調(diào)用onNewIntent
比如設(shè)置Main為singleTask,打開Main后,再打開A,B,C三個(gè)默認(rèn)standard的Activity,由C打開Main,則棧中只剩下Main。

注意1:singleTask 和 taskAffinity
每個(gè)activity都有taskAffinity屬性,如果沒有顯式的指明taskAffinity,那么就等于Application指明的taskAffinity,而如果Application也沒有指明,那么該taskAffinity的值就等于應(yīng)用的包名。
默認(rèn)情況下singleTask所在的任務(wù)棧就是app啟動(dòng)的默認(rèn)棧,而當(dāng)設(shè)置了taskAffinity后,那么啟動(dòng)設(shè)置singleTask的activity,那么就會(huì)新創(chuàng)建一個(gè)棧來存放activity。
<activity android:name=".A"
android:launchMode="singleTask"
android:taskAffinity=".myNewTask"/>
這樣一個(gè)app就存在兩個(gè)任務(wù)棧。
啟動(dòng)流程是這樣的:
a)判斷當(dāng)前任務(wù)棧taskAffinity是否為.myNewTask,如果是的話,直接將該activity置于當(dāng)前任務(wù)棧棧頂
b)如果當(dāng)前任務(wù)棧taskAffinity不為.myNewTask,則檢查是否存在一個(gè)任務(wù)棧taskAffinity值為.myNewTask,如存在,則將該任務(wù)棧移到前臺(tái),并將該activity置于棧頂
c)如果并不存在一個(gè)任務(wù)棧taskAffinity值為.myNewTask,那么將會(huì)為該activity創(chuàng)建一個(gè)新的任務(wù)棧,該任務(wù)棧顯示在前臺(tái)
注意2:SingleTask && MAIN啟動(dòng)
每個(gè)app都會(huì)存在一個(gè)啟動(dòng)的activity,即點(diǎn)擊圖標(biāo)進(jìn)入的第一個(gè)activity。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
當(dāng)此時(shí)的MainActivity設(shè)置android:launchMode="singleTask"時(shí),會(huì)產(chǎn)生這樣一個(gè)現(xiàn)象:回退Home后重新打開app,總是會(huì)重新打開,而不記錄最后打開的頁面。
這個(gè)其實(shí)就是由于棧內(nèi)存在一個(gè)實(shí)例導(dǎo)致,因?yàn)橹耙呀?jīng)啟動(dòng)過app,也就是后臺(tái)存在這個(gè)app的任務(wù)棧,那么回退到Home再重新打開,首先會(huì)找到這個(gè)App對(duì)應(yīng)的任務(wù)棧,而后由于啟動(dòng)的MainActvity是SingleTask,在任務(wù)棧中已經(jīng)存在,那么會(huì)直接使用這個(gè)存在的實(shí)例,又因?yàn)?code>MainActivity處于棧底,那么啟動(dòng)這個(gè)已經(jīng)存在的實(shí)例會(huì)將上方所有的activity都清除出棧
注意3:allowTaskReparenting && taskAffinity
activity的taskAffinity,默認(rèn)指向application的taskAffinity,默認(rèn)是mainfest里package值,也就是常說的包名。
allowTaskReparenting這個(gè)屬性很有意思,可以將activity由一個(gè)task遷移到另一個(gè)task,而將activity遷移到另一個(gè)task是又條件的:
- 首先指定
android:allowTaskReparenting="true" - 指定
android:taskAffinity=".xx"這個(gè).xx并不是你應(yīng)用本身的taskAffinity,或者不同應(yīng)用默認(rèn)taskAffinity本身就不同,可以不指定taskAffinity - 發(fā)生
Task Reset,點(diǎn)擊Home再啟動(dòng)APP可發(fā)生Task Reset
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.taskb">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".OtherTaskActivity"
android:allowTaskReparenting="true"
android:taskAffinity=".othertask"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
taskAffinity屬性是遷移后的所指棧。
根據(jù)上述Mainfest配置,執(zhí)行流程:
- 首先點(diǎn)擊APP圖標(biāo)啟動(dòng)
MainActivity,創(chuàng)建一個(gè)taskAffinity為com.example.taskb的任務(wù)棧,MainActivity入棧。
啟動(dòng)APP后棧狀態(tài) - 由
MainActivity啟動(dòng)OtherTaskActivity,那么他還是會(huì)在應(yīng)用的棧中入棧。
啟動(dòng)OtherTaskActivity - 點(diǎn)擊Home,發(fā)生
Task Reset,應(yīng)用com.example.taskb任務(wù)棧處于后臺(tái),由于OtherTaskActivity屬性allowTaskReparenting="true" && taskAffinity=".othertask",所以此時(shí)這個(gè)應(yīng)用后臺(tái)將存在兩個(gè)任務(wù)棧。
兩個(gè)任務(wù)棧 - 點(diǎn)擊APP圖標(biāo)重新啟動(dòng)應(yīng)用,
com.example.taskb任務(wù)棧處于前臺(tái),顯示MainActivity,com.example.taskb.othertask任務(wù)棧處于后臺(tái),不顯示。當(dāng)按回退鍵時(shí),com.example.taskb任務(wù)棧清除,com.example.taskb.othertask任務(wù)棧回到前臺(tái),顯示OtherTaskActivity。
注意4: taskAffinity起作用只有兩種情況
- 設(shè)置
launchMode="singleTask"或者intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - 設(shè)置
allowTaskReparenting=true并滿足前面所說的情況。
應(yīng)用場(chǎng)景:
主activity設(shè)置SingleTask,退出應(yīng)用清除棧。適合閃屏頁+主頁模式,閃屏頁為MAIN入口,跳轉(zhuǎn)到主頁前finish閃屏頁,主頁啟動(dòng)模式為SingleTask,可以實(shí)現(xiàn)保存應(yīng)用狀態(tài)。
4. singleInstance 棧內(nèi)有且只有一個(gè)實(shí)例,并可復(fù)用
任務(wù)棧中只有一個(gè)activity,如果存在,那么直接使用實(shí)例,調(diào)用onNewIntent方法。


