Android中的Launch Mode詳情
一. 多任務(wù)和Task、啟動(dòng)模式
Android 手機(jī)在早期,下方通常會(huì)內(nèi)置三個(gè)實(shí)體的觸摸按鍵,分別是:桌面
、菜單
、返回
。大概在Android 5.0 之后,Android開(kāi)始流行系統(tǒng)內(nèi)置虛擬按鍵,其中的菜單
被替換成了多任務(wù)
,一旦我們按下多任務(wù)按鍵,一個(gè)個(gè)的任務(wù)快照就以流的形式展現(xiàn)在屏幕之上。
隨著Google對(duì)多任務(wù)更好地支持,越來(lái)越多的廠商將正面的實(shí)體按鍵,替換成了虛擬按鍵,也將菜單按鍵刪除,替換成了多任務(wù)視圖按鈕,顯然多任務(wù)在之后的Android版本中是非常重要的一個(gè)概念。
甚至由于虛擬按鍵的出現(xiàn),一些特定型號(hào)的手機(jī)在下方可能會(huì)形成奇怪的五層下巴:
我們將這一個(gè)個(gè)的任務(wù)叫做Task
,多任務(wù)視圖中,會(huì)顯示各個(gè)Task頂層Activity的快照(之所以是快照,是因?yàn)锳ctivity不一定是存活的,有可能它只是一張圖片。)Task中,以回退棧的形式堆疊Activity。
每個(gè)Task,對(duì)應(yīng)一個(gè)名稱,如果我們不去設(shè)置,那么就是我們Application的應(yīng)用包名,默認(rèn)情況下,start一個(gè)新的Activity就會(huì)被裝入該Task當(dāng)中,然后在這個(gè)Task中進(jìn)行堆疊,新打開(kāi)的Activity在上方,用戶可以通過(guò)按下返回鍵回退到上一個(gè)Activity當(dāng)中。
每個(gè)Activity,都有一個(gè)TaskAffinity屬性,標(biāo)志了它想去的Task是哪一個(gè),如果你不填寫(xiě),那么通常是默認(rèn)的目標(biāo)棧,但是要注意的是,TaskAffinity需要和LaunchMode搭配在一起使用:
- 如果不設(shè)置LaunchMode(即采用默認(rèn)的Standard),那么你從ActivityA中啟動(dòng)一個(gè)設(shè)置了TaskAffinity的ActivityB,你會(huì)發(fā)現(xiàn)它不生效,它仍然在當(dāng)前包名的ActivityA的對(duì)應(yīng)的任務(wù)棧當(dāng)中。
- 如果你將TaskAffinity配置和LaunchMode = SingleTask一起使用,在你打開(kāi)了ActivityB時(shí),你按下多任務(wù)按鈕,你會(huì)發(fā)現(xiàn)一個(gè)App出現(xiàn)了兩個(gè)Task,即兩個(gè)回退棧,這兩個(gè)回退棧的分別對(duì)應(yīng)ActivityA和ActivityB的對(duì)應(yīng)的Task的回退棧:
它們擁有相同的項(xiàng)目名稱,因?yàn)橹皇敲麨閞outer的app模塊下的兩個(gè)不同TaskAffinity的Activity,不過(guò)這個(gè)TaskAffinity并不顯示在多任務(wù)視圖當(dāng)中。
二. 四種啟動(dòng)模式詳解
所以,四種啟動(dòng)模式,對(duì)應(yīng)的具體的啟動(dòng)情況如下:
1. Standard
在當(dāng)前的Task的回退棧中
,啟動(dòng)一個(gè)Activity實(shí)例,放在棧頂。
在這種模式下,使用TaskAffinity是無(wú)效的。即使填寫(xiě)了TaskAffinity,最終也會(huì)被創(chuàng)建在執(zhí)行啟動(dòng)命令的Activity對(duì)應(yīng)的Task棧的棧頂,而不是TaskAffinity對(duì)應(yīng)的Task的棧頂。
2. SingleTask
在TaskAffinity指定的退回棧中
,嘗試啟動(dòng)一個(gè)Activity實(shí)例:
- 如果指定的回退棧中,含有該Activity相同類型的實(shí)例,那么就回調(diào)onNewIntent()方法,告知原先已經(jīng)存在的實(shí)例X,然后回調(diào)onResume()方法,并將原先實(shí)例上方的實(shí)例全部移除出回退棧,這樣一來(lái),原先已經(jīng)存在的實(shí)例X就會(huì)出現(xiàn)在棧頂。
- 如果指定的回退棧中,不包含Activity相同類型的實(shí)例,那么就在棧頂創(chuàng)建,走正常的生命周期回調(diào):
onCreate()->onStart()->onResume()
- 如果TaskAffinity對(duì)應(yīng)的Task都不存在的情況下,會(huì)先去創(chuàng)建目標(biāo)Task,再走創(chuàng)建Activity實(shí)例的流程,最后壓入棧頂。
- 如果沒(méi)有指定TaskAffinity,那么就指定為當(dāng)前調(diào)用啟動(dòng)操作的Activity的Task,將Activity壓入該Task回退棧的棧頂。
注意:
創(chuàng)建在當(dāng)前Task和其它Task的Activity跳轉(zhuǎn)動(dòng)畫(huà)是不相同的。
SingleTask + TaskAffinity實(shí)際上也是一種全局的單例,因?yàn)樗膭?chuàng)建結(jié)果最終都會(huì)將Activity創(chuàng)建在指定的TaskAffinity的Task下。即使不填寫(xiě)TaskAffinity,也會(huì)只在當(dāng)前Activity對(duì)應(yīng)的Task下創(chuàng)建或者復(fù)用唯一的一個(gè)實(shí)例。
3. SingleTop
在TaskAffinity指定的退回棧中
,嘗試在啟動(dòng)一個(gè)Activity實(shí)例。和SingleTask類似,但是復(fù)用條件稍有不同。僅在目標(biāo)Task的回退棧的頂部含有相同類型的Activity時(shí),觸發(fā)復(fù)用,回調(diào)onNewIntent和onResume。
4.SingleInstance
在TaskAffinity指定的退回棧中
,創(chuàng)建一個(gè)Activity實(shí)例,但是,一個(gè)Task中僅允許一個(gè)Activity存在,如果兩個(gè)Activity對(duì)應(yīng)的TaskAffinity是相同的,例如從A中以SingleInstance啟動(dòng)B,B中以SingleInstance啟動(dòng)C,BC的TaskAffinity是相同的。
這種情況下,如果我們?cè)贑中,呼出多任務(wù)視圖菜單,我們會(huì)發(fā)現(xiàn),此時(shí)棧中只有C和A對(duì)應(yīng)的Task,B對(duì)應(yīng)的Task并不存在,你可能會(huì)認(rèn)為B和C在同一個(gè)棧中,B在C之下。但是如果你以另外一種方式: 在C中,呼出多任務(wù)菜單,回到C后,然后點(diǎn)擊返回,你認(rèn)為應(yīng)該回到B,但是你會(huì)發(fā)現(xiàn),直接退到桌面了。 或者你在C中,直接按回車,你會(huì)發(fā)現(xiàn)從C->B和從B->A的跳轉(zhuǎn)過(guò)場(chǎng)動(dòng)畫(huà),都是Task間的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),而不是Task內(nèi)部的跳轉(zhuǎn)動(dòng)畫(huà)。
這涉及到另外我們就要講另外一個(gè)問(wèn)題,Task間跳轉(zhuǎn)時(shí),Task間的堆疊問(wèn)題(Task疊在另一個(gè)Task上面),而打開(kāi)多任務(wù)列表或者按下Home鍵會(huì)導(dǎo)致堆疊被破壞。
B和C即使有同一個(gè)TaskAffinity命名,并且根據(jù)我們說(shuō)的一個(gè)Task中僅允許一個(gè)Activity存在,C打開(kāi)時(shí),B應(yīng)該被關(guān)閉,從多任務(wù)上來(lái)看,似乎也是這樣的,因?yàn)橹挥蠧和A的Task在多任務(wù)視圖中,但是我們確實(shí)又可以從C的Task返回到B的Task,在任務(wù)棧中我們又看不到B的身影,這可以下一個(gè)結(jié)論:多任務(wù)視圖中的不可見(jiàn)的Task不一定不存在,如果發(fā)生SingleInstance + TaskAffinity沖突的情況,例如:
Activity A和Activity B都是SingleInstance的,并且又都是設(shè)置了TaskAffinity為 com.example.newTask。如果既要保證AB都在同一個(gè)Task中,又要保證該Task只能有一個(gè)>Activity,那么就會(huì)導(dǎo)致沖突。
經(jīng)過(guò)測(cè)試,當(dāng)沖突發(fā)生時(shí),Android會(huì)為兩個(gè)Activity都創(chuàng)建一個(gè)Task,但是同一時(shí)間,只有一個(gè)能>在多任務(wù)視圖中被看見(jiàn),但是如果順序啟動(dòng):A、B,是可以在B中回退到A中的。并且回退的動(dòng)畫(huà)是Task 間切換的動(dòng)畫(huà)。
那么Task中可見(jiàn)的Activity一定在運(yùn)行嗎?答案也是否定的,如果我們?cè)贏ctivityA按下返回鍵,退回到桌面,我們此時(shí)打開(kāi)多任務(wù),我們會(huì)發(fā)現(xiàn),ActivityA的Task的快照仍然保留在多任務(wù)視圖之上,但是它此時(shí)已經(jīng)“死了”,我們點(diǎn)擊它,實(shí)際上是創(chuàng)建了一個(gè)新的ActivityA。所以,多任務(wù)視圖中中的不可見(jiàn)的Task不一定不存在,多任務(wù)視圖中可見(jiàn)的Activity也不一定就是Running狀態(tài)的。
三. Task間堆疊與Task Reparenting
1. Task間堆疊
考慮如下的兩個(gè)任務(wù)棧間切換場(chǎng)景:
我們需要從ActivityC中啟動(dòng)Activity E,此時(shí)ActivityE的啟動(dòng)模式被設(shè)置成了:SingleTask,TaskAffinity是Task2。ActivityE被啟動(dòng)之后,在屏幕上顯示出來(lái)了。
基于這個(gè)狀態(tài),接下來(lái)有幾個(gè)問(wèn)題:
- 問(wèn)題1:如果此時(shí)按多次返回鍵,會(huì)發(fā)生什么?
- 問(wèn)題2:如果此時(shí)先按Home回到桌面,再?gòu)亩嗳蝿?wù)列表打開(kāi)Task1,顯示的是ActivityC還是ActivityE?
- 問(wèn)題3:如果此時(shí)先按Home回到桌面,再?gòu)亩嗳蝿?wù)列表打開(kāi)Task2,顯示的是ActivityE,如果此時(shí)不不斷地按返回鍵,會(huì)發(fā)什么?
- 問(wèn)題4:如果此時(shí)先打開(kāi)多任務(wù)列表,再按返回,返回Task2,此時(shí)顯示的是ActivityE。如果此時(shí)不不斷地按返回鍵,會(huì)發(fā)什么?
對(duì)于問(wèn)題1,答案是:Activity E/D/C/B/A同時(shí)出棧,A出棧之后回到桌面。 對(duì)于問(wèn)題2,答案是:顯示的是ActivityC 對(duì)于問(wèn)題3、4,答案是:Activity E/D出棧,D出棧之后,回到桌面,而不是Activity C。
問(wèn)題1的原因是因?yàn)?,Task2的任務(wù)被打開(kāi)之后,整個(gè)Task2成為了優(yōu)先顯示的Task,被堆在屏幕之上,一旦Task2的Activity退完了,Task1可以無(wú)縫地銜接上:
就好像Task2被堆在了Task1之上,這樣的堆疊,保證了用戶交互的連貫性。
這樣的Task間的堆疊跳轉(zhuǎn)特性適用于用戶跳轉(zhuǎn)某個(gè)頁(yè)面之后,按返回鍵不想馬上跳回原Activity的一種情況。
但是,這種堆疊僅限于Task跳轉(zhuǎn)剛剛發(fā)生的情況,一旦用戶進(jìn)行了:
- Home鍵返回桌面
- 切出多任務(wù)視圖
這類的操作,將頂端的Task變?yōu)楹笈_(tái)Task之后,那么這種「堆疊」就會(huì)立刻失效,并且不再恢復(fù),所以在問(wèn)題3、4中,最后返回的是桌面,而不是Task1的ActivityC,這種堆疊被破壞了,Task1和Task2又重新回到平級(jí)的狀態(tài)了。
但是在最新的API32版本中,切到Android 自帶的多任務(wù)視圖并不會(huì)導(dǎo)致Task2重新被移動(dòng)到Task1平級(jí)的位置,這意味著,你從多任務(wù)切回來(lái)之后,在ActivityE按下返回,仍然會(huì)回退到到Task1的ActivityC之上。
如果有一些場(chǎng)景,你希望打開(kāi)ActivityE之后不退到D,直接退到C那應(yīng)該怎么做呢?其實(shí)不設(shè)置SingleTask就好了,默認(rèn)的就是這種情況,比如外賣平臺(tái)支付訂單之后,付款軟件的的付款結(jié)果頁(yè)的一個(gè)實(shí)例就被留在了外賣軟件的Task中。
2. Task Reparenting/Task重定父級(jí)
通常來(lái)說(shuō),一個(gè)Activity被裝進(jìn)一個(gè)Task的棧之后,就不會(huì)去移動(dòng)了,但是我們可以借助Task Reparenting來(lái)做父級(jí)的重新指定。
例如上述的例子中,我們隊(duì)ActivityE設(shè)置:allowTaskReparenting。我們從Task1中的ActivityC啟動(dòng)ActivityE時(shí),ActivityE會(huì)啟動(dòng)在Task1中。
一旦我們切到桌面,再重新從桌面圖標(biāo)重新打開(kāi)Task1時(shí),我們會(huì)發(fā)現(xiàn)ActivityE從Task1中消失了,而從桌面打開(kāi)Task2對(duì)應(yīng)的App圖標(biāo)時(shí),會(huì)發(fā)現(xiàn),ActivityE重新回到了自己的Task2中,例如我們按照如下定義:
<activity android:name=".ActivityE" android:allowTaskReparenting="true" android:exported="false" android:taskAffinity="com.example.anotherTask" />
如果我們?cè)谀J(rèn)的,和包名相同的com.rEd.router
下創(chuàng)建該MainActivityE,那么此時(shí)的MainActivity會(huì)在當(dāng)前的com.rEd.router
下創(chuàng)建該MainActivityE實(shí)例,然后我退到桌面,再重新在桌面的App圖標(biāo)打開(kāi)App,我會(huì)發(fā)現(xiàn),啟動(dòng)的App的當(dāng)前的Activity變成了ActivityC,而不是ActivityE:
而且,多任務(wù)視圖中的Task2中的快照是空白的,我們點(diǎn)開(kāi)Task2,發(fā)現(xiàn)ActivityE回到了它taskAffinity指定的Task中:
另外,如果同時(shí)設(shè)置了allowTaskReparenting=true和LaunchMode,那么LaunchMode會(huì)優(yōu)先生效,Activity會(huì)直接創(chuàng)建在其他的Task中。
到此這篇關(guān)于Android中的Launch Mode詳情的文章就介紹到這了,更多相關(guān)Android Launch Mode內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化
網(wǎng)上有很多大拿分享的關(guān)于Android性能優(yōu)化的文章,主要是通過(guò)各種工具分析,使用合理的技巧優(yōu)化APP的體驗(yàn),提升APP的流暢度,但關(guān)于內(nèi)存優(yōu)化的文章很少有看到。下面是我在實(shí)踐過(guò)程中使用的一些方法,很多都是不太成熟的項(xiàng)目,只是將其作為一種處理方式分享給大家。2016-08-08Android SurfaceView運(yùn)行機(jī)制剖析--處理切換到后臺(tái)再重新進(jìn)入程序時(shí)的異常
本文主要介紹Android SurfaceView運(yùn)行機(jī)制,這里整理了詳細(xì)的資料來(lái)講解SurfaceView的運(yùn)行原理,并附示例代碼參考,有需要的小伙伴可以參考下2016-08-08Android實(shí)現(xiàn)注冊(cè)頁(yè)面(攜帶數(shù)據(jù)包跳轉(zhuǎn))
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)注冊(cè)頁(yè)面,點(diǎn)擊注冊(cè)按鈕跳轉(zhuǎn)到另一個(gè)頁(yè)面并顯示輸入信息,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04android:descendantFocusability方法介紹
開(kāi)發(fā)中很常見(jiàn)的一個(gè)問(wèn)題,項(xiàng)目中的listview不僅僅是簡(jiǎn)單的文字,常常需要自己定義listview,問(wèn)題就出現(xiàn)了,可能會(huì)發(fā)生點(diǎn)擊每一個(gè)item的時(shí)候沒(méi)有反應(yīng),無(wú)法獲取的焦點(diǎn)2012-11-11Android HorizontalScrollView左右滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android HorizontalScrollView左右滑動(dòng)效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02在Android系統(tǒng)源碼中預(yù)置APK的方法
今天小編就為大家分享一篇關(guān)于在Android系統(tǒng)源碼中預(yù)置APK的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12Android 使用AsyncTask實(shí)現(xiàn)斷點(diǎn)續(xù)傳
這篇文章主要介紹了Android 使用AsyncTask實(shí)現(xiàn)斷點(diǎn)續(xù)傳的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-05-05Android自定義相機(jī)實(shí)現(xiàn)自動(dòng)對(duì)焦和手動(dòng)對(duì)焦
這篇文章主要為大家詳細(xì)介紹了android手動(dòng)實(shí)現(xiàn)相機(jī)自動(dòng)和手動(dòng)對(duì)焦功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android Toolbar自定義標(biāo)題標(biāo)題居中的實(shí)例代碼
這篇文章主要介紹了Android Toolbar自定義標(biāo)題 標(biāo)題居中的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08