Activity的啟動(dòng)模式
Activity作為安卓四大組件之首擎颖,它的確非常重要,上篇文章我們介紹了Activity的生命周期观游,其實(shí)Activity的啟動(dòng)模式也是一個(gè)難點(diǎn)搂捧,有時(shí)為了項(xiàng)目的特殊需求,我們需要使用到Activity的啟動(dòng)模式懂缕,下面我們就具體的介紹Activity的啟動(dòng)模式和標(biāo)志位允跑。
1.1Activity的LaunchMode(啟動(dòng)模式)
首先我們開(kāi)介紹一下Activity為什么需要啟動(dòng)模式。在默認(rèn)情況下搪柑,多次啟動(dòng)同一個(gè)Activity聋丝,系統(tǒng)會(huì)創(chuàng)建多個(gè)實(shí)例,并把這些實(shí)例一一的壓入任務(wù)棧中工碾。當(dāng)我們單擊返回鍵的時(shí)候弱睦,就會(huì)一一回退。我們知道在數(shù)據(jù)結(jié)構(gòu)中渊额,棧是先進(jìn)后出的况木。其實(shí)就是每按一次返回鍵,銷(xiāo)毀一個(gè)Activity旬迹,就會(huì)有一個(gè)Activity實(shí)例出棧火惊,直到棧內(nèi)為空的時(shí)候,系統(tǒng)將任務(wù)棻伎眩回收矗晃。這就是Activity在默認(rèn)情況下的啟動(dòng)方式。在這里我們會(huì)不會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題:多次啟動(dòng)同一個(gè)Activity系統(tǒng)竟然多次創(chuàng)建實(shí)例宴倍,這是不是感覺(jué)很奇葩?在學(xué)習(xí)設(shè)計(jì)模式的時(shí)候,我們都知道有單實(shí)例模式仓技,我想Android系統(tǒng)也不會(huì)那么笨鸵贬。是的,Android系統(tǒng)其實(shí)早就知道這一點(diǎn)了脖捻,所以Android系統(tǒng)在設(shè)計(jì)的時(shí)候提供了啟動(dòng)模式來(lái)修改這種默認(rèn)的啟動(dòng)行為阔逼。目前有四種啟動(dòng)模式:standard、singleTop地沮、singleTask嗜浮、singleInstance羡亩。下面就對(duì)這四種啟動(dòng)模式進(jìn)行一一的介紹:
(1)standard
standard:標(biāo)準(zhǔn)模式,這是系統(tǒng)默認(rèn)的啟動(dòng)模式危融。每次啟動(dòng)一個(gè)Activity就會(huì)創(chuàng)建一個(gè)Activity的實(shí)例畏铆。不管這個(gè)實(shí)例是否存在都會(huì)進(jìn)行創(chuàng)建。每次創(chuàng)建Activity的生命周期吉殃,也都是符合正常情況下Activity的生命周期辞居。創(chuàng)建一個(gè)Activity實(shí)例,就會(huì)把這個(gè)實(shí)例壓入任務(wù)棧中蛋勺,一個(gè)任務(wù)椡咴睿可以有多個(gè)Activity實(shí)例,同一個(gè)Activity的各個(gè)實(shí)例也可以對(duì)應(yīng)不同的任務(wù)棧抱完。在標(biāo)準(zhǔn)模式下:一個(gè)Activity由誰(shuí)啟動(dòng)的贼陶,實(shí)例就會(huì)壓入啟動(dòng)者所在的任務(wù)棧,簡(jiǎn)單的說(shuō)就是:假如一個(gè)Activity A 啟動(dòng)了 Activity B 那么B的實(shí)例就會(huì)壓入A所在的任務(wù)棧中巧娱。在這我們提一點(diǎn)碉怔,不知道讀者是否遇到這樣一個(gè)問(wèn)題。使用ApplicationContext去啟動(dòng)一個(gè)Activity會(huì)報(bào)如下錯(cuò)誤:
接下來(lái)筆者將介紹如何重現(xiàn)這樣的錯(cuò)誤信息:
通過(guò)上面知道我們?nèi)绻肁ctivity A ?通過(guò)標(biāo)準(zhǔn)模式去啟動(dòng)Activity B ?B的實(shí)例會(huì)壓入A的實(shí)例所在的任務(wù)棧中家卖。這個(gè)很容易理解眨层。這里介紹的都是通過(guò)Activity去啟動(dòng)另一個(gè)Activity是沒(méi)問(wèn)題的,因?yàn)锳ctivity啟動(dòng)者會(huì)有對(duì)應(yīng)的任務(wù)棧上荡。假如我們通過(guò)廣播接受者去啟動(dòng)一個(gè)Activity呢?如下圖所示:
通過(guò)標(biāo)準(zhǔn)模式趴樱,以上的代碼能啟動(dòng)一個(gè)Activity嗎,還是會(huì)報(bào)上面所說(shuō)的錯(cuò)誤酪捡?筆者告訴大家叁征,答案是:
《1》當(dāng)這個(gè)廣播接收者是通過(guò)手動(dòng)在A(yíng)ctivity ?A 中注冊(cè)的情況下是可以的,上面代碼中的Context屬于A(yíng)ctivity A 對(duì)象逛薇,這個(gè)時(shí)候通過(guò)啟動(dòng) Activity B其實(shí)就是通過(guò)這個(gè)Activity A 啟動(dòng)的捺疼,B的實(shí)例就會(huì)壓入到Activity B 實(shí)例所在的任務(wù)棧。這樣不會(huì)報(bào)錯(cuò)永罚,正常啟動(dòng)啤呼。
《2》當(dāng)這個(gè)廣播接收者是通過(guò)在Manifest中注冊(cè)的情況下是不可的,就會(huì)報(bào)上面所說(shuō)的錯(cuò)誤信息呢袱。這個(gè)時(shí)候上面代碼
中的Context是屬于ReceiverRestrictedContext@41a7c2b8廣播接受者的官扣,廣播接受者沒(méi)有對(duì)應(yīng)的任務(wù)棧,因此不能通過(guò)context去啟動(dòng)一個(gè)Activity羞福。當(dāng)然假如一定要啟動(dòng)Activity也是可以的惕蹄,我們可以進(jìn)行設(shè)置標(biāo)志位:FLAG_ACTIVITY_NEW_TASK。其實(shí)就是啟動(dòng)一個(gè)新的任務(wù)棧,把啟動(dòng)的Activity壓入這個(gè)新的任務(wù)棧中卖陵。其實(shí)這種啟動(dòng)模式遭顶,就是下面會(huì)介紹的另一種啟動(dòng)模式:sinleTask啟動(dòng)模式
(2)singleTop
singleTop:棧頂復(fù)用模式。同樣用例子說(shuō)明:當(dāng)前任務(wù)棧里的情況是A B C D ,A位于棧頂泪蔫,C位于棧底棒旗。
《1》通過(guò)singleTop模式Activity D啟動(dòng)Activity A,這個(gè)時(shí)候不會(huì)創(chuàng)建A的實(shí)例鸥滨,只是繼續(xù)使用棧頂中的A嗦哆,調(diào)用了onNewInstane方法,通過(guò)此方法的參數(shù)婿滓,我們可以取出當(dāng)前請(qǐng)求的數(shù)據(jù)老速。既然不會(huì)重新創(chuàng)建A,所以A的生命周期方法:onCreate、onStart凸主、onResume方法不會(huì)被調(diào)用橘券。這個(gè)時(shí)候棧內(nèi)的情況是:A B C D。
《2》同樣通過(guò)singleTop模式Activity D啟動(dòng)Activity B 卿吐,由于B不在棧頂旁舰,這個(gè)時(shí)候會(huì)創(chuàng)建B的實(shí)例,開(kāi)始B的生命周期方法嗡官。棧內(nèi)情況:B A B C D,這個(gè)應(yīng)該很容易理解箭窜,筆者在這就不多說(shuō)了。
(3)singleTask
singleTask:棧內(nèi)復(fù)用模式衍腥。通過(guò)棧頂復(fù)用模式的學(xué)習(xí)磺樱,我想學(xué)習(xí)棧內(nèi)復(fù)用模式應(yīng)該會(huì)比較容易的理解。這是一種單實(shí)例模式婆咸,在這種情況下竹捉,如果通過(guò)singleTask模式去啟動(dòng)一個(gè)Activity A,首先系統(tǒng)會(huì)尋找是否存在A(yíng)所需要的任務(wù)棧尚骄,如果不存在块差,則創(chuàng)建任務(wù)棧,再創(chuàng)建A的實(shí)例壓入任務(wù)棧中倔丈。如果存在憨闰,則檢查棧中是否存在A(yíng)的實(shí)例,如果不存在需五,創(chuàng)建A的實(shí)例壓入棧中起趾。如果存在,則把A移動(dòng)到棧頂警儒,假如之前棧內(nèi)之前的情況是 D C B A ,D位于棧頂,這個(gè)時(shí)候把A移到棧頂蜀铲,由于singleTask具有clearTop效果边琉,會(huì)導(dǎo)致A 上面的全部出棧,所以把A移到棧頂之后棧內(nèi)的情況是A记劝。下面用例子來(lái)說(shuō)明:
《1》棧S1 的情況是 A B C, A位于棧頂变姨。創(chuàng)建D,并且所需的任務(wù)棧是S2。這個(gè)時(shí)候棧S2和實(shí)例均不存在厌丑,則需要?jiǎng)?chuàng)建任務(wù)棧S2和D實(shí)例定欧,把D實(shí)例壓入S2中。
《2》假如創(chuàng)建D所需的棧是S1,這個(gè)時(shí)候怒竿,直接創(chuàng)建D實(shí)例砍鸠,壓入S1中。S1的情況是:D A B C
《3》假如S1的情況是 A B C D 耕驰,創(chuàng)建D所需的任務(wù)棧是S1爷辱,這個(gè)時(shí)候S1已經(jīng)存在,D也存在朦肘。所以直接把D移到棧頂饭弓,最后由于clearTop模式,S1的情況是A
(4)singleInstance
singleInstance:單實(shí)例模式,這種模式具有singleTask模式所有的特性媒抠。和singleTask不同的是弟断,通過(guò)singleInstance模式啟動(dòng)的Activity所在的任務(wù)棧只能有一個(gè)實(shí)例。而通過(guò)singleTask啟動(dòng)的Activity所在任務(wù)椗可可以有不同的實(shí)例阀趴,只是同一個(gè)Activity只能有一個(gè)實(shí)例。
Activity的Flag(標(biāo)志位)
在上面介紹Activity的啟動(dòng)模式的時(shí)候冲秽,我們?cè)?jīng)提到舍咖,假如通過(guò)ApplicationContext去啟動(dòng)一個(gè)Activity是不行的。會(huì)報(bào):
AndroidRuntimeException: Calling startActivity() from outside of an Activity锉桑,Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
錯(cuò)誤的信息就是需要一個(gè)標(biāo)志位的意思排霉。
下面我們就介紹一下標(biāo)志位:標(biāo)志位有很多,這里主要分析一下比較常用的標(biāo)志位民轴。有的標(biāo)志位可以設(shè)置Activity的啟動(dòng)模式攻柠。有的標(biāo)志位可以影響Activity 的運(yùn)行狀態(tài)。大部分情況下我們不需要去指定標(biāo)志位后裸,對(duì)標(biāo)志位理解即可瑰钮。
(1)FLAG_ACTIVITY_NEW_TASK
該標(biāo)志位的作用是設(shè)置Activity的啟動(dòng)模式是singleTask 即棧內(nèi)復(fù)用模式。
(2)FLAG_ACTIVITY_SINGLE_TOP
該標(biāo)志位的作用是設(shè)置Activity的啟動(dòng)模式是singleTop模式 即單實(shí)例模式
(3)FLAG_ACTIVITY_CLEAR_TOP
該標(biāo)志位的作用是啟動(dòng)一個(gè)Activity微驶,該Activty實(shí)例所在的任務(wù)棧中所有在該實(shí)例之上的實(shí)例都要出棧浪谴。本身singleTop啟動(dòng)模式就具有該clearTop特性