Activity 的 LaunchMode
默認(rèn)情況下,多次啟動(dòng)同一個(gè) Activity 時(shí),系統(tǒng)會(huì)創(chuàng)建多個(gè)實(shí)例并把它們一一放入任務(wù)棧中,當(dāng)我們按 back 健時(shí),這些 Activity 會(huì)一一回退。任務(wù)棧是一種“后進(jìn)先出”的棧結(jié)構(gòu),即每按一下 back 健就會(huì)有一個(gè) Activity 出棧,直到??諡橹?,當(dāng)棧中無任何 Activity 時(shí),系統(tǒng)會(huì)回收這個(gè)任務(wù)棧。
四種啟動(dòng)模式:standard、singleTop、singleTask 和 singleInstance。下面介紹各種啟動(dòng)模式的含義及使用例子。
一、standard – 默認(rèn)模式
standard:標(biāo)準(zhǔn)模式也是系統(tǒng)的默認(rèn)模式。每次啟動(dòng)一個(gè) Activity 都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在。這種模式下,誰啟動(dòng)了這個(gè) Activity,那么這個(gè) Activity 就運(yùn)行在啟動(dòng)它的那個(gè) Activity 所在的棧中。如 Activity A 啟動(dòng)了 Activity B(B 是標(biāo)準(zhǔn)模式),那么 B 就會(huì)進(jìn)入到 A 所在的棧中。
當(dāng)我們用 ApplicationContext 去啟動(dòng) standard 模式的 Activity 時(shí)會(huì)報(bào)錯(cuò),錯(cuò)誤如下:
android.util.AndroidRuntimeException: Calling startActivity() from outside
of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.
Is this really what you want?
這是因?yàn)?standard 模式的 Activity 默認(rèn)會(huì)進(jìn)入啟動(dòng)它的 Activity 所屬的任務(wù)棧中,由于非 Activity 類型的 Context (如 ApplicationContext)并沒有所屬的任務(wù)棧,所以就出問題了。解決方法是為待啟動(dòng) Activity 指定 FLAG_ACTIVITY_NEW_TASK 標(biāo)記位,這樣啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)新的任務(wù)棧,這時(shí)待啟動(dòng)的 Activity 是以 singleTask 模式啟動(dòng)的
二、singleTop – 棧頂復(fù)用模式
singleTop 棧頂復(fù)用模式。如果新 Activity 已經(jīng)位于任務(wù)棧的棧頂,那么此 Activity 不會(huì)被重新創(chuàng)建,同時(shí)它的 onNewIntent 方法會(huì)被調(diào)用,通過此方法的參數(shù)可以取出當(dāng)前請(qǐng)求的信息。需要注意的是,這個(gè) Activity 的 onCreate、onStart 不會(huì)被系統(tǒng)重新調(diào)用,因?yàn)樗]有發(fā)生改變。如果新 Activity 的實(shí)例已經(jīng)存在但不是位于棧頂,那么新 Activity 仍然會(huì)重建。
適合接收通知啟動(dòng)的內(nèi)容顯示頁面,當(dāng)收到多條新聞推送時(shí),用于展示新聞的 Activity 設(shè)置成此模式,根據(jù)傳來的 Intent 數(shù)據(jù)顯示不同的新聞信息,不會(huì)啟動(dòng)多個(gè) Activity。
三、singleTask – 棧內(nèi)復(fù)用模式
singleTask:棧內(nèi)復(fù)用模式。這是一種單實(shí)例模式,在這種模式下,只要 Activity 在一個(gè)棧中存在,那么多次啟動(dòng)此 Activity 都不會(huì)重新創(chuàng)建實(shí)例,復(fù)用時(shí)會(huì)將它上面的 Activity 全部出棧,同時(shí)它的 onNewIntent 方法會(huì)被調(diào)用。這個(gè)過程存在一個(gè)任務(wù)棧匹配,因?yàn)檫@個(gè)模式啟動(dòng)時(shí)會(huì)在自己需要的任務(wù)棧中尋找實(shí)例,這個(gè)任務(wù)棧通過 taskAffinity 屬性指定,如果這個(gè)任務(wù)棧不存在,則會(huì)創(chuàng)建這個(gè)任務(wù)棧。
taskAffinity 標(biāo)識(shí)了一個(gè) Activity 所需的任務(wù)棧的名字,默認(rèn)情況下,所有 Activity 所需的任務(wù)棧的名字為應(yīng)用的包名。我們可以為每個(gè) Activity 都單獨(dú)指定 TaskAffinity 屬性,這個(gè)屬性必須不能和包名相同,否則就相當(dāng)于沒有指定。TaskAffinity 屬性主要和 singleTask 啟動(dòng)模式或者 allowTaskReparenting 屬性配對(duì)使用。另外,任務(wù)棧分為前臺(tái)任務(wù)棧和后臺(tái)任務(wù)棧,后臺(tái)任務(wù)棧中的 Activity 處于暫停狀態(tài),用戶可以通過切換將后臺(tái)任務(wù)棧再次調(diào)到前臺(tái)。
適合作為程序入口點(diǎn),例如瀏覽器的主界面,不管從多少個(gè)應(yīng)用啟動(dòng)瀏覽器,只會(huì)啟動(dòng)主界面一次,其余情況都會(huì)走 onNewIntent,并且會(huì)清空主界面上的其它頁面
四、singleInstance – 單實(shí)例模式
singleInstance:?jiǎn)螌?shí)例模式。該模式除了具備 singleTask 模式的所有特性外,該模式的 Activity 只能單獨(dú)的位于一個(gè)任務(wù)棧中,具有全局唯一性,即整個(gè)系統(tǒng)中只有這一個(gè)實(shí)例,由于棧內(nèi)復(fù)用的特性,后續(xù)的請(qǐng)求均不會(huì)創(chuàng)建新的Activity實(shí)例,除非這個(gè)特殊的任務(wù)棧被銷毀了。以singleInstance模式啟動(dòng)的Activity在整個(gè)系統(tǒng)中是單例的,如果在啟動(dòng)這樣的Activiyt時(shí),已經(jīng)存在了一個(gè)實(shí)例,那么會(huì)把它所在的任務(wù)調(diào)度到前臺(tái),重用這個(gè)實(shí)例
如何指定 Activity 的啟動(dòng)模式?
(1)通過 AndroidMenifest 為 Activity 指定啟動(dòng)模式,如下所示:
<activity
android:name=".activity.protocol.ProtocolActivity"
android:launchMode="singleTask"/>
(2)通過 Intent 中設(shè)置標(biāo)志位來為 Activity 指定啟動(dòng)模式,如下所示:
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
優(yōu)先級(jí)上第二種優(yōu)先級(jí)高于第一種,當(dāng)兩種同時(shí)存在時(shí),以第二種方式為準(zhǔn);這兩種方式的限定方式不同,第一種無法直接為 Activity 設(shè)定 FLAG_ACTIVITY_CLEAR_TOP 標(biāo)識(shí),第二種無法為 Activity 指定 singleInstance 模式。
Activity 常用 Flags
FLAG_ACTIVITY_NEW_TASK
為 Activity 指定 singleTask 啟動(dòng)模式,效果和在 XML 中指定該模式相同
FLAG_ACTIVITY_SINGLE_TOP
為 Activity 指定 singleTop 啟動(dòng)模式,效果和在 XML 中指定該模式相同
FLAG_ACTIVITY_CLEAR_TOP
具有此標(biāo)記的 Activity,當(dāng)它啟動(dòng)時(shí),在同一個(gè)任務(wù)棧中所有位于它上面的 Activity 都要出棧
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有這個(gè)標(biāo)記的 Activity 不會(huì)出現(xiàn)在歷史 Activity 的列表中,在某些情況下我們不希望用戶通過歷史列表回到我們的 Activity 的時(shí)候這個(gè)標(biāo)記比較有用。它等同于在 XML 中指定 Activity 的屬性 android:excludeFromRecents="true"。