這篇文章的背景來自於週五的時候,有一個小夥伴私聊我一個問題。說實話讓我「頗爲震驚」:android
這裏在分析啓動模式的基礎上,或回答他的問題,或驗證他的猜測。主要集中在這幾個地方,你們也能夠在看文章的時候先問問本身能不能回答出啦:shell
問題一: app
驗證部分:2.3ide
問題二: 學習
這是一個頗有趣的現象,估計你們都沒有注意到吧~解答部分:2.1ui
問題三: spa
這裏的答案是上面問題的一個延伸,也會在2.1部分中解釋。設計
問題四: 3d
這個問題應該也是衆多小夥伴感到「有悖常識」的地方,其實官方文檔已經解答了這個問題。解答部分2.4code
在學習Ativity的過程當中,Task的概念多少是咱們無可迴避的概念。從官方文檔中咱們基本可以理解明白Task和Stack的關係...
一張很經典的圖:
Activity以Task的概念聚合在一塊兒,並且不管是單一Task中的多個Activity仍是多個Task混在一塊兒,它們都是以Stack形式所包含在一塊兒。
可是,不知道有多少小夥伴在dumpsys activity以後陷入了另外一個疑惑:TaskRecord是啥?
其實Task和TaskRecord是同一個概念。TaskRecord
是framework層的一個類。它就是Task這個抽象概念的代碼實現。讓咱們看一個dumpsys的圖:
使用命令:adb shell dumpsys activity
使用命令:adb shell dumpsys activity activities | sed -En -e '/Stack #/p' -e '/Running activities/,/Run #0/p'
這兩幅圖是同一個Activity棧的場景,可是能夠看出來TaskRecord和Task是同一個概念。
啓動模式,這個我們都很熟悉,張口就來:
關於它們的概念,這裏就不作累述,畢竟複製粘貼也沒有任何意義。(後文會詳細闡明四種模式和taskAffinity以及各類Flags的做用)可是這裏特別貼一張官方的圖,你們感覺一下:
這表明了Google對singleTask以及singleInstance的態度,也就是說這倆種模式並非設計給主流Activity的,所以使用這倆種模式前須要真正的理解它們。
demo很簡單定義了5個Activity:
<activity android:name=".test.TestMainLauncherActivity" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".test.TestSingleInstanceLauncherActivity" android:launchMode="singleInstance"/>
<activity android:name=".test.TestTopAffinityLauncherActivity" android:launchMode="singleTop" android:taskAffinity="app.mdove"/>
<activity android:name=".test.TestSingleTaskLauncherActivity" android:launchMode="singleTask" />
<activity android:name=".test.TestSingleTaskAffinityLauncherActivity" android:taskAffinity="app.mdove.singletask" android:theme="@style/MyAppTheme" android:launchMode="singleTask" />
複製代碼
首先,我們看一下文檔對android:taskAffinity
的描述:
與 Activity 有着類似性的任務。從概念上講,具備同一類似性的 Activity 歸屬同一任務(從用戶的角度來看,則是歸屬同一「應用」)。任務的類似性由其根 Activity 的類似性肯定。
類似性肯定兩點內容 :
allowTaskReparenting
屬性,這個屬性比較有意思,可是我們先按下不表)僅從文檔的描述來看,彷佛有些雨裏霧裏。因此接下來我們經過實際代碼來確認taskAffinity
的含義。
這裏我先貼結論:
一、taskAffinity
單獨設置在standard、singleTop是沒有任何意義的。由於taskAffinity
的效果依賴啓動Activity時使用FLAG_ACTIVITY_NEW_TASK標籤。(也就是上文官網提到的那一條)
二、以taskAffinity
模式啓動成功後,新的Activity會處在新的Task。而且由此啓動的Activity(無taskAffinity
)皆處於此Task。
接下來我們用戶實際效果驗證這個結論(操做路線):
TestMainLauncherActivity啓動TestTopAffinityLauncherActivity
而後在TestTopAffinityLauncherActivity上啓動TestMainLauncherActivity
在TestMainLauncherActivity上經過FLAG_ACTIVITY_NEW_TASK標籤,啓動TestTopAffinityLauncherActivity
最後由TestTopAffinityLauncherActivity啓動TestMainLauncherActivity
不知道你們可否從腦海裏,構建出Task的結構?這裏我貼一下dumpsys的圖:
簡單解釋一下:
一、看一下Task #21,因爲咱們啓動TestTaskAffinityLauncherActivity時沒有增長FLAG_ACTIVITY_NEW_TASK,所以它的Task並無變化。
二、在Task #22中,TestTaskAffinityLauncherActivity經過FLAG_ACTIVITY_NEW_TASK直接進入了新的Task中,而且後續啓動的Activity也和它「共處一室」。
若是咱們經過FLAG_ACTIVITY_NEW_TASK
+ taskAffinity
啓動了一個Activity1,而且用這個Activity1啓動一個普通的Activity2,若是此時在Activity2上啓動Activity1,會發現沒有任何反應!!
其實不能說是沒反應,「系統的反應」是吧Activity1所在的task移到了前臺。說時候這個細節我一直沒有注意到,要不是這個小夥伴提醒,我可能很長一段時間都不會注意到這個問題。
復現了這個現象的時候,我也很懵逼。第一想法是翻一翻文檔,是否是文檔早有提示...而後大失所望,並無發現任何關於此現象的解答。
既然文檔沒有解釋,那我們就翻一翻源碼...不過我剛點擊去FLAG_ACTIVITY_NEW_TASK
,有看到了大段的註釋,其中有這麼一段話:
這裏註釋着:若是這個Task中run了你要satrt的Activity,那麼將不會起一個新的Activity,而是將此Task移至前臺。這個註釋正是咱們遇到的現象...
而對於FLAG_ACTIVITY_NEW_TASK
來講,它本質是去找要啓動的taskAffinity
所聲明的Task,若是沒有,它的處理方式和普通起一個Activity沒有本質區別,因此就不會作特殊處理。
那麼問題來了:爲啥要這麼設計的?實話我也不知道...我猜應該是爲了提供這麼一種能力,畢竟PM的想象力是無限的。(若是咱們想要在這種狀況下,顯示要start的Activity,別忘了還有FLAG_ACTIVITY_CLEAR_TOP呢)
singleTask下,只有設置了taskAffinity纔會爲對應啓動的 Activity建立一個新的Task。而且後續的Activity同進入這個Task。
我相信這個你們都很熟悉...
你們對singleInstance,應該都比較清晰。由於比較特殊:永遠獨享一個Task。
這裏咱們看倆個有意思的特性:
一、以經過singleInstance
啓動的Activity會默認帶上FLAG_ACTIVITY_NEW_TASK
。所以若是經過singleInstance
啓動帶有taskAffinity
的singleTop
,會起新的Task。
這裏有一個細節:僅帶有
FLAG_ACTIVITY_NEW_TASK
,不帶有taskAffinity
。是會將啓動的Activity放到根棧裏。所以這裏會出現一個細節:
那就是若是由singleInstance
啓動一個Activity,因爲會放入根棧,那麼此時根Task會被拉到前臺,那麼此時back鍵就會顯示根Task下面的Activity,也就是以下的狀況:
此時back掉TestMainLaunncherAcctivity
,顯示的仍然是TestMainLaunncherAcctivity
。由於他們同屬一個#43的棧。
不少博客/資料都會提到一個說法:FLAG_ACTIVITY_NEW_TASK
+ FLAG_ACTIVITY_CLEAR_TOP
= singleTask
。
可是這句話並不全對,這不是我說的,而是文檔說的:
注意紅線圈住的內容:也就是意味着用這倆個FLAG對於standard
模式來講,並不會觸發onNewIntent()
,而是銷燬重建。
OK,這篇文章到此就結束了...因此叭叭整了不少內容,可是從Google的態度也能看出來,singleTask已經singleInstance是一種慎重使用的提示。
彷佛standard和singleTop已經能夠知足咱們的需求了...不過,彷佛真的有點小瞧四種啓動模式了...