最近遇到了一個(gè)小問題膘壶,在我使用了多種Activity啟動(dòng)模式的時(shí)候,重新打開其中的一個(gè)Activity會(huì)啟動(dòng)另一個(gè)我已經(jīng)停止的Activity凄诞,從而調(diào)用了一些已經(jīng)失效的方法導(dǎo)致程序崩潰廊散。
1 問題重現(xiàn)
由于項(xiàng)目工程復(fù)雜娶耍,Activity名稱不夠直觀,我新建了一個(gè)ActivityTaskTest 工程來重現(xiàn)遇到的問題擦耀。
ActivityA是工程的主活動(dòng)棉圈。因?yàn)橐恍┍匾脑颍?ActivityA的啟動(dòng)模式是SingleInstance的。ActivityA可以啟動(dòng)ActivityB眷蜓,ActivityB沒有設(shè)置任何啟動(dòng)模式分瘾,即默認(rèn)的standard啟動(dòng)模式。在ActivityB中吁系,將會(huì)啟動(dòng)一個(gè)ServiceA芹敌。 ServiceA中啟動(dòng)一個(gè)了一個(gè)ActivityC,由于Activity是在非Acitivity環(huán)境下啟動(dòng)的垮抗,需要設(shè)置 FLAG_ACTIVITY_NEW_TASK標(biāo)簽(這里就是我們討論的重點(diǎn)氏捞,稍候會(huì)詳細(xì)分析)。當(dāng)ActivityC完成任務(wù)后會(huì)重新跳轉(zhuǎn)到ActivityA冒版。
最后液茎,見證奇葩的時(shí)刻到了,我們點(diǎn)擊ActivityA的啟動(dòng)ActivityB的button辞嗡,ActivityC出現(xiàn)在了我們的眼前捆等,而不是ActivityB!续室!這一刻我仿佛劉謙附體栋烤,但在我發(fā)現(xiàn)我身邊并沒有董卿之后,我深刻地意識(shí)到了我是一個(gè)工程師挺狰,不能搞這些裝神弄鬼的事情明郭。ok买窟,Let's find out what‘s going on with our precious app!
2 FLAG_ACTIVITY_NEW_TASK使用分析
關(guān)于Activity啟動(dòng)模式和Activity Task的內(nèi)容推薦一篇非常好的文章:Android中Activity四種啟動(dòng)模式和taskAffinity屬性詳解薯定。這篇文章已經(jīng)講得非常詳細(xì)了始绍,這里就不再贅述了,偷個(gè)懶哈哈话侄。
如果你已經(jīng)看了文章亏推,你應(yīng)該已經(jīng)知道問題的所在了,對(duì)年堆,就是這個(gè)該死的taskAffinity吞杭。簡(jiǎn)單的說,就是我們雖然使用了FLAG_ACTIVITY_NEW_TASK標(biāo)簽去啟動(dòng)了ActivityC变丧,但是因?yàn)槲覀兺私oActivity設(shè)置taskAffinity這個(gè)小婊砸篇亭,所以導(dǎo)致ActivityC的taskAffinity值和ActivityB一樣都是默認(rèn)的包名。所以我們啟動(dòng)ActivityC的時(shí)候系統(tǒng)將ActivityC壓入了ActivityB所在的task锄贷。我們可以使用adb shell dumpsys activity activities 指令看下一在我們重新從A中啟動(dòng)B之前译蒂,Task的情況:
我們可以看到正如我們所想的,ActivityC和ActivityB在一個(gè)Task中谊却,由于ActivityA是singleInstance模式柔昼,所以A只能做一輩子單身狗了。那么為什么我們明明啟動(dòng)的是B炎辨,怎么會(huì)出現(xiàn)C呢捕透?
我們來先看看Google官方文檔對(duì)于FLAG_ACTIVITY_NEW_TASK是怎么說的:
在新的 task 中啟動(dòng) activity。如果要啟動(dòng)的 activity 已經(jīng)運(yùn)行于某 task 中碴萧,則那個(gè) task 將調(diào)入前臺(tái)乙嘀,最后保存的狀態(tài)也將恢復(fù),activity 將在onNewIntent()中接收到這個(gè)新 intent破喻。
這個(gè)過程與前一節(jié)所述的"singleTask"launchMode模式值相同虎谢。
注意文檔中的內(nèi)容,“如果要啟動(dòng)的 activity 已經(jīng)運(yùn)行于某 task 中曹质,則那個(gè) task 將調(diào)入前臺(tái)婴噩,最后保存的狀態(tài)也將恢復(fù)”,注意這里是所在task被直接調(diào)入前臺(tái)羽德,也就是說B所在的整個(gè)Task將被移入前臺(tái)几莽。這就解釋了為什么我們?nèi)?dòng)B而出現(xiàn)的是C了≌玻看起來我們好像大功告成了章蚣,但是,等等姨夹,總覺得哪里有點(diǎn)不太對(duì)勁纤垂,我們的ActivityB明明沒有設(shè)置啟動(dòng)模式啊矾策,你這個(gè)是FLAG_ACTIVITY_NEW_TASK標(biāo)簽,我沒用啊洒忧,我讀書多你可別騙我。
仔細(xì)想想應(yīng)該是ActivityA的singleInstance的鍋够颠。
我們?cè)賮砜纯碐oogle官方文檔對(duì)于singleInstance是怎么說的吧:
“singleTask”和“singleInstance”模式同樣只在一個(gè)方面有差異: “singleTask”Activity 允許其他 Activity 成為其任務(wù)的組成部分熙侍。 它始終位于其任務(wù)的根位置,但其他 Activity(必然是“standard”和“singleTop”Activity)可以啟動(dòng)到該任務(wù)中履磨。 相反蛉抓,“singleInstance”Activity 則不允許其他 Activity 成為其任務(wù)的組成部分。它是任務(wù)中唯一的 Activity剃诅。 如果它啟動(dòng)另一個(gè) Activity巷送,系統(tǒng)會(huì)將該 Activity 分配給其他任務(wù) — 就好像 Intent 中包含F(xiàn)LAG_ACTIVITY_NEW_TASK一樣。
看到最后一句矛辕,終于可以結(jié)案了笑跛。也就是說,當(dāng)一個(gè)被設(shè)置為singleInstance的Activity去啟動(dòng)其他的Activity的時(shí)候聊品,其默認(rèn)是自帶FLAG_ACTIVITY_NEW_TASK標(biāo)簽的飞蹂。
3 總結(jié)
1、FLAG_ACTIVITY_NEW_TASK標(biāo)簽必須配合taskAffinity屬性使用翻屈,如果不設(shè)置taskAffinity屬性值陈哑,將不會(huì)生成新task。
2伸眶、當(dāng)從啟動(dòng)模式為singleInstance的Acitivity中啟動(dòng)新的Acitivity時(shí)惊窖,新的Activity自帶FLAG_ACTIVITY_NEW_TASK標(biāo)簽。
心得:官方文檔是個(gè)好東西厘贼。