我覺得理解了Android四大啓動模式,直到被打臉

前言

這篇文章的背景來自於週五的時候,有一個小夥伴私聊我一個問題。說實話讓我「頗爲震驚」:android

  • 一、問題看起來很簡單,關於Activity啓動模式...可是的確裏邊的細節觸及了個人知識盲區。
  • 二、這個小夥伴發了一個詳細的pdf文檔,看起來也是常常總結~點贊這種學習態度~

這裏在分析啓動模式的基礎上,或回答他的問題,或驗證他的猜測。主要集中在這幾個地方,你們也能夠在看文章的時候先問問本身能不能回答出啦:shell

問題一: app

驗證部分:2.3ide

問題二: 學習

這是一個頗有趣的現象,估計你們都沒有注意到吧~解答部分:2.1ui

問題三: spa

這裏的答案是上面問題的一個延伸,也會在2.1部分中解釋設計

問題四: 3d

這個問題應該也是衆多小夥伴感到「有悖常識」的地方,其實官方文檔已經解答了這個問題。解答部分2.4code

正文

1、理解概念

在學習Ativity的過程當中,Task的概念多少是咱們無可迴避的概念。從官方文檔中咱們基本可以理解明白Task和Stack的關係...

一張很經典的圖:

Activity以Task的概念聚合在一塊兒,並且不管是單一Task中的多個Activity仍是多個Task混在一塊兒,它們都是以Stack形式所包含在一塊兒。

可是,不知道有多少小夥伴在dumpsys activity以後陷入了另外一個疑惑:TaskRecord是啥?

1.一、Stack、Task、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是同一個概念

1.二、launchModel

啓動模式,這個我們都很熟悉,張口就來:

  • standard
  • singleTop
  • singleTask
  • singleInstance

關於它們的概念,這裏就不作累述,畢竟複製粘貼也沒有任何意義。(後文會詳細闡明四種模式taskAffinity以及各類Flags的做用)可是這裏特別貼一張官方的圖,你們感覺一下:

這表明了Google對singleTask以及singleInstance的態度,也就是說這倆種模式並非設計給主流Activity的,所以使用這倆種模式前須要真正的理解它們。

2、從demo中完全理解

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" />
複製代碼

2.一、理解taskAffinity

首先,我們看一下文檔對android:taskAffinity的描述:

與 Activity 有着類似性的任務。從概念上講,具備同一類似性的 Activity 歸屬同一任務(從用戶的角度來看,則是歸屬同一「應用」)。任務的類似性由其根 Activity 的類似性肯定。

類似性肯定兩點內容 :

  • Activity 更改父項後的任務(請參閱 allowTaskReparenting 屬性,這個屬性比較有意思,可是我們先按下不表)
  • 以及經過 FLAG_ACTIVITY_NEW_TASK 標記啓動 Activity 時,用於容納該 Activity 的任務。

僅從文檔的描述來看,彷佛有些雨裏霧裏。因此接下來我們經過實際代碼來確認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呢)

2.二、理解singleTask

singleTask下,只有設置了taskAffinity纔會爲對應啓動的 Activity建立一個新的Task。而且後續的Activity同進入這個Task。

我相信這個你們都很熟悉...

2.三、理解singleInstance

你們對singleInstance,應該都比較清晰。由於比較特殊:永遠獨享一個Task

這裏咱們看倆個有意思的特性:

一、以經過singleInstance啓動的Activity會默認帶上FLAG_ACTIVITY_NEW_TASK。所以若是經過singleInstance啓動帶有taskAffinitysingleTop,會起新的Task。

這裏有一個細節:僅帶有FLAG_ACTIVITY_NEW_TASK,不帶有taskAffinity。是會將啓動的Activity放到根棧裏。所以這裏會出現一個細節:

那就是若是由singleInstance啓動一個Activity,因爲會放入根棧,那麼此時根Task會被拉到前臺,那麼此時back鍵就會顯示根Task下面的Activity,也就是以下的狀況:

此時back掉TestMainLaunncherAcctivity,顯示的仍然是TestMainLaunncherAcctivity。由於他們同屬一個#43的棧。

2.四、FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP = singleTask?

不少博客/資料都會提到一個說法:FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP = singleTask

可是這句話並不全對,這不是我說的,而是文檔說的:

注意紅線圈住的內容:也就是意味着用這倆個FLAG對於standard模式來講,並不會觸發onNewIntent(),而是銷燬重建。

尾聲

OK,這篇文章到此就結束了...因此叭叭整了不少內容,可是從Google的態度也能看出來,singleTask已經singleInstance是一種慎重使用的提示。

彷佛standard和singleTop已經能夠知足咱們的需求了...不過,彷佛真的有點小瞧四種啓動模式了...

我是一個應屆生,最近和朋友們維護了一個公衆號,內容是咱們在從應屆生過渡到開發這一路所踩過的坑,以及咱們一步步學習的記錄,若是感興趣的朋友能夠關注一下,一同加油~

我的公衆號:鹹魚正翻身
相關文章
相關標籤/搜索