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