Activity 的 Task 以及 launchMode 研究

dreamist發表於2016-08-25

android 的 Task 和 Back Stack

Task 簡介

android 中的Task是一系列Activity的集合,使用者通過操作多個Activity來完成一個Task。一個Task的具體實現就是一個包含多個Activity的棧,當使用者進入某一個app的時候,app的入口Activity被壓入棧底,使用者每進入一個Activity,該Activity都會被新增到該app的棧頂。Task中的每個Activity的順序是不會重新排列的,Task的動作只有進棧和出棧。在android中點選OverView Screen的按鈕可以進到OverView Screen的介面,如圖:

android_overview_screen

圖中展示出了現在系統中所有的Task,最前面的是前臺的Task,其他的是後臺的Task。

更多的關於Task的資訊請參考:官方文件

Back Stack 回退棧簡介

回退棧指的是當使用者點選返回鍵的時候,android系統會一個接一個地pop出Task中所保留的Activity。回退棧更多地是表示邏輯上的一個概念,可以理解成Task的邏輯抽象。

Task 和 Back Stack的關係

Task 可以理解成 Back Stack的一種具體實現。除了包括Activity的Task之外,android系統中的Fragment也是具有回退棧的,但是Fragment並沒有像Activity的Task那樣的東西。

Task 與 Activity

Activity 的例項化是依賴於Task的,它必須被載入到某一個Task中才可以,從另一個角度來看,Task就是Activity的容器。android 系統在啟動一個Activity的時候,會在系統中尋找是否存在符合該Activity 的自身屬性 和 啟動該Activity的Intent的要求的Task(具體怎麼尋找,會在下面討論),有的話直接在在該Task中例項化一個Activity,沒有的話則需要新建一個Task,再在其中對Activity進行例項化。

系統如何給一個Activity尋找它的歸屬Task?

Task 有一個關鍵的屬性affinity(中文為親緣、近緣等,但是感覺譯成中文並不合適,遂用英文直接闡述了)。android中每一個Activity也都有一個叫做affinity的屬性,Activity的affinity值可以在mainfest配置檔案中通過標籤的andrid:taskAffinity屬性指定,如果沒有指定的話則預設使用該Activity所在app的包名。每一個Task的affinity屬性值等於該Task的root Activity 的affinity值。具有相同affnity的Activity在被系統例項化的時候更加容易被駕到同一個Task中。當然,這只是比較關鍵點額一個點,android系統在給Activity找Task的時候會受到非常多因素的影響,有來自Intent例項的眾多flag,還有來自mainfest配置檔案中Activity的眾多配置資訊,這是一個複雜的過程,本文不進行深入討論。

launchMode 研究

launchMode簡介

launchMode 是Activity類的一個屬性,該屬性包括4個具體值:standard、singleTop、singleTask、singleInstance。我們都知道,android系統中啟動Activity是通過Intent例項進行的,當系統收到一個Intent的例項需要去啟動指定的Activity的時候,android系統會根據目標Activity的該屬性值來決定是要要建立新的該Activity例項以及如何在Task中建立該Activity的例項。這就是launchMode這個屬性的作用。

launchMode 分為兩個類別

  • 普通型別:使用者常用的啟動模式型別,大部分Activity都是這兩種啟動模式。包括standdard 和 singleTop 兩種啟動模式
  • 特殊型別:具有特殊的行為的啟動模式,只針對特殊需求的使用。包括 singleTask 和 singleInstance 兩種啟動模式

官方文件對4中啟動模式描述如下:

使用場景 啟動模式 是否可以有多個例項? 簡介
普通型別 standard 系統預設的啟動模式,當系統接收到一個Intent例項去啟動一個standard模式的Activity時,系統總是會在目標棧的頂部建立一個新的activity例項,並把Intent的例項傳進去。
普通型別 singleTop 視具體情況 當系統接收到一個Intent例項去啟動一個singleTop模式的Activity時,如果在目標棧的頂部存在一個該Activity的例項的話,那麼系統就會重用這個Activity的例項而不建立新的例項,並回撥該Activity的onNewIntent(Intent intent)方法把新的Intent例項當作方法引數傳遞進去;如果在目標棧的頂部沒有該Activity的例項的話系統將會在新建一個Activity例項,與standard的行為就一樣了。
特殊型別 singleTask 當系統接收到一個Intent例項去啟動一個singleTask模式的Activity時,如果不存在該Activity的例項的話,系統會先建立一個新的Task,並在該Task底部裡面建立一個該Activity的例項,隨後把Intent例項傳遞進去;如果已經存在一個該Activity的例項的話,系統就不會再建立新的例項,那麼系統就會重用這個Activity的例項而不建立新的例項,並回撥該Activity的onNewIntent(Intent intent)方法把新的Intent例項當作方法引數傳遞進去,同時,該Activity例項所在的Task將會被調到前臺
特殊型別 singleInstance 類似於singleTask,唯一不同的地方在於,singleInstance的Activity不允許自己的Task中存在其他的Activity例項,也就是說singleInstance的Activity永遠是Task中唯一的一個Activity例項

launchMode 的定義

  • 靜態定義:在目標Activity的mainfest中使用標籤靜態定義
  • 動態定義:在啟動目標Activity的Intent物件中使用不同的flag定義,可以用來定義launchMode的flag有以下三個:
flag 對應的launchMode
FLAG_ACTIVITY_NEW_TASK singleTask
FLAG_ACTIVITY_SINGLE_TOP singleTop
FLAG_ACTIVITY_CLEAR_TOP 無對應的launchMode

note:在Activity A啟動 Activity B的時候,假如B已經在mainfest檔案中定義過launchMode,此時的Intent物件中也有定義launchMode,這時候以Activity A start B的時候以Intent中定義的為準。

典型使用場景

launchMode 使用場景
standard 絕大多數標準的跳轉
singleTop 新聞閱讀類的內容頁面,假如在一個新聞頁面又開啟了另一個新聞頁面,這時候Task的頂端已經有該Activity的例項了,就不用再初始化了
singleTask 通知欄啟動其他app,一般都會在Intent中加上FLAG_ACTIVITY_NEW_TASK,相當於使用了singleTask

一些會影響 Task 行為的 mainfest中activity的屬性

屬性名稱 可取值 意義
android:taskAffinity 一個存在的包名 表示該Activity與該包名具有親緣,在該Activity啟動的時候,會優先選擇被新增進已經存在的具有親緣關係的Task中。
android:allowTaskReparenting true, false 如果一個Activity的該屬性值是true,它先被一個外部app啟動了,此時它存在於那個呼叫它的app的Task中,當與它有親緣關係的Task被後臺帶回到前臺的時候,系統會把它換到該Task中;如果值為false的話,就不會發生移動。
android:alwaysRetainTaskState true,false 正常情況下,當一個Task在後臺待了很久之後,系統會把該Task的非root Activity都清除,只保留root Activity。但是假如root Activity的該屬性值被設定為true的話,系統會保留該Task中所有的Activity。

android中Activity的launchMode總結

  • launchMode的作用,是讓開發者可以在一定程度上決定自己的Activity該如何被系統例項化,從而來滿足開發者的開發需求
  • launchMode一共分為兩類,一類是普通開發者最常用的:standard 和 singleTop,其中standard是官方定義的最常見的也是預設的系統對Activity進行例項化的行為模式,singleTop在standard的基礎上進行了點小的改動,當目標棧的頂部有Activity的例項的時候將不再對該Activity進行初始化而是直接使用現成的Activity例項,因為有很多的使用場景是standard不太合適而singleTop比較合適的;另一個類別的是普通開發者不常使用的,singleTask 和 singleInstance,官方並不推薦在通常的場景中使用該類的launchMode,只有在極少數特殊的情形中才去使用,該類launchMode的特點是Activity在系統中只會有一個例項存在,不同的地方在於singleInstance的Activity會獨佔一個Task,而singleTask的則不會。
  • launchMode可以在mainfest檔案中靜態地設定,同時,在程式碼中,通過Intent的幾個flag也可以達到設定launchMode的目的。假如某一次啟動一個Activity的時候,在Intent中指定了一個launchMode,而且該Activity本身已經在mainfest檔案中設定了launchMode,那麼這個時候系統會以Intent中指定的為準。android系統這樣設計的目的,我想是為了讓整個系統的Activity可以更方便地讓其它Activity(可以不再一個app內)進行呼叫,這與android的開放的態度是一致的。

參考資料:
https://developer.android.com/guide/components/tasks-and-back-stack.html

https://developer.android.com/guide/topics/manifest/activity-element.html#aff

http://droidyue.com/blog/2015/08/16/dive-into-android-activity-launchmode/index.html

http://www.it610.com/article/5751414.htm

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

Activity 的 Task 以及 launchMode 研究 Activity 的 Task 以及 launchMode 研究

相關文章