介紹
Application
Android 開發中有四大組件:Activity、Service、BroadCastReceiver、ContentProvider。
一個 Android 軟件必定有一個 Application,在程序打開時第一個執行的就是 Application.onCreatea()
進行初始化話。
Task
Task 是在程序運行時存放 Activity 對象的棧。 只針對 Activity,不同的應用的 Activity 對象可以存放在一個 Task 裏面。
程序內每個 Activity 都能指定在哪個 Task 棧活動,通過設置taskAffinity
屬性
<activity
android:name=".SecondActivity"
android:taskAffinity="com.example.demo.second" >
不設置的話默認是程序包名,設置爲""
空字符串的話,表示這個Activity
不屬於任何棧。
結合棧的 LIFO 性質,Android 中 Task 這個概念保證了跨應用操作的連貫性。
如下場景:
- 應用X 中的某 Activity(
X1Activity
) 位於Task(id = 104)
中 - 應用Y 中有個 Activity(
Y1Activity
) 開發了打開意圖,同過Uri指定就能訪問,設置默認啓動模式 X1Activity
通過 Uri 方式匿名打開Y1Activity
- 此時
Y1Activity
位於Task(id = 104)
中,且在棧的位置在X1Activity
上部 Y1Activity
出棧後,手機直接展示X1Activity
界面
實際操作場景如:某新聞應用分享時,調起郵箱發送界面,操作成功後按下返回鍵就回到了新聞應用界面。
Progress
Progress 指的是進程。進程是操作系統內核中的一個概念,表示直接受內核調度的執行單位。
在 Android 系統中,軟件是運行在虛擬機(Dalvik、ART)中運行的,可以認爲一個運行中的虛擬機實例佔有一個進程。
在默認情況下,一個 Android 應用程序所有組件運行在同一個進程中。
Task:Activity 設置 taskAffinity
Activity 啓動模式
standard
ActivityA 啓動模式設置爲 standard。
標準啓動模式,也是activity的默認啓動模式。ActivityA 可以被多次實例化,Task 棧中可同時存在多個 ActivityA 的實例。
當前 Task 棧狀態:A –> B
再次啓動 ActivityA。重新創建 ActivityA 對象,調起 onCreate() 方法
當前 Task 棧狀態:A –> B –> A
singleTop
ActivityB 啓動模式設置爲 singleTop。
維持 Task 棧頂部相鄰實例的 ActivityB 單一,棧中不會連續出現同一個 ActivityB 的實例。
(一)
當前 Task 棧狀態:A –> B
再次啓動 ActivityB。複用 Task 棧頂 ActivityB 的實例。調起 onNewIntent()
方法傳入 Intent 對象。
當前 Task 棧狀態:A –> B
(二)
當前 Task 棧狀態:B –> A
再次啓動 ActivityB。操作等同於 standard 啓動模式。
當前 Task 棧狀態:B –> A –> B
singleTask
ActivityC 啓動模式設置爲 singleTask
維持 Task 全棧只有一個 ActivityC 的實例。同一時刻在系統中只會存在一個這樣的Activity實例。
啓動 ActivityC 時,因爲要複用 ActivityC 對象,並且要將此對象放至 Task 棧頂(當前顯示 Activity 的實例一定是在所在 Task 棧的棧頂)。
- 新開一個棧 TaskNew(若棧不存在) ,將 ActivityC 的實例 cNew 置於新 TaskNew 棧頂,cNew 啓動的其他 Activity 也存放與 TaskNew 中
- 銷燬在 Task 棧中位於 ActivityC 實例之上的所有Activity實例
(一)
當前 Task 棧狀態:A –> C –> B –> A
再次啓動 ActivityC 。複用 Task 棧中 ActivityC 的實例。調起 onNewIntent()
方法傳入 Intent 對象。
當前 Task 棧狀態:A –> C
(二)
設置 ActivityC 的 taskAffinity 屬性爲 “com.xxx.xx”,相當於指定棧名。
當前 Task 棧狀態:B –> A
啓動 ActivityC 。 建立新棧 TaskNew,創建 ActivityC 的實例。調起 onCreate()。
當前 Task 棧狀態:B –> A
當前 TaskNew 棧狀態:C
singleInstance
ActivityD 啓動模式設置爲 singleInstance
總是在新的 TaskNew 棧中開啓 ActivityD 並實例化,且 TaskNew 中有且只有這一個實例。其他的 Activity 實例在其他的 Task 棧中。
同一時刻在系統中只會存在一個這樣的Activity實例。
當開啓 ActivityD 時:
- 沒開啓:創建新的 TaskNew ,並 TaskNew 中開啓 ActivityD 並實例化
- 已開啓:進入已存在的 TaskNew棧,並調用此棧中 ActivityD 的實例的 onNewIntent()
方法傳入 Intent 對象。
-
Activity 設置 taskAffinity 屬性
配合啓動模式,一般情況下分析
(一)taskAffinity 屬性配合 singleTask 啓動模式
singleTask 要求 設置此屬性的 Activity 在同一時刻在系統中只會存在一個這樣的Activity實例。
同時用 taskAffinity 指定了 Task 棧名的話
無論從什麼渠道開啓此 Activity 都會在指定棧中打開。
(二)taskAffinity 屬性配合 standard、singleTop 啓動模式
不設置:Intent.FLAG_ACTIVITY_NEW_TASK
必定存在 Activity 實例進行startActivity()
動作,參考此實例,並在同一個 Task 棧中運行。
設置:Intent.FLAG_ACTIVITY_NEW_TASK
在指定的棧中進行 Activity 活動,此棧不存在則創建。
同時具備設置的啓動模式的性質,standard總是新建實例 或 singleTop棧頂實例複用
(三)taskAffinity 屬性配合 singleInstance 啓動模式
在單獨的 Task 棧中活動,此棧有且只有一個 Activity 實例(設置 singleInstance 啓動模式的 Activity)
若在開啓前已存在同名 Task 棧,照樣開啓新棧運行,滿足 singleInstance 啓動模式特性。
不同的 App 的 Activity 在同一個棧,打開、回退流程栗子
- AppA 的 ActivityA
(Task1)
- 手機按下 Home 進入主界面
- 打開 AppB 默認啓動 ActivityB1
(Task2)
- ActivityB1 打開 ActivityB2
(Task1)
(和ActivityA一個棧) - 此時顯示的是 AppB 的 ActivityB2
- 按下返回鍵
- 此時顯示的是 AppA 的 ActivityA
所以在 Android 系統中按下返回鍵是對當前 Task 棧進行出棧操作,然後顯示此 Task 棧臨近棧頂的元素。
特殊情況
App 默認啓動 ActivityL 設置 taskAffinity,同名 Task 棧在開啓 App 之前已存在。
- 爲 standard、singleTop設置時。 會顯示當前 Task 棧頂 Activity,不會創建 ActivityL 的實例(不走生命週期)。
- 爲 singleTask 設置時。會創建 ActivityL 實例展示並放在棧頂,按返回鍵顯示棧頂下一個 Activity。
- 爲 singleInstance 設置時。就算同名的棧存在也會新建一個棧,滿足此棧只有一個 Activity 實例,且只能是 ActivityL 的實例
理解 Task,taskAffinity 和 Activity 啓動模式後,就能更合理的設計 Android 軟件了。
Process:使用多進程
若軟件使用了多進程,在AndroidManifest.xml
:
<service
android:name=".ServerA"
android:process=":serverA" />
<service
android:name=".ServerB"
android:process=":serverB" />
會發現 Application.onCreate()
方法會多次調用,這是應用程序裏面就有多個 Application 對象了。
所以我們在做 Application 初始化的時候要區分當前進程做初始化。
@Override
public void onCreate() {
super.onCreate();
// 默認進程的動作
if (getPackageName().equals(getCurrentProcessName(getApplicationContext()))) {
startService(new Intent(this, ServerA.class));
startService(new Intent(this, ServerB.class));
// do
}
// ServerA 進程的動作
else if ((getPackageName() + ":serverA").equals(getCurrentProcessName(getApplicationContext()))) {
// do
}
// ServerB 進程的動作
else if ((getPackageName() + ":serverB").equals(getCurrentProcessName(getApplicationContext()))) {
// do
}
}
N個進程,N個虛擬機,N個 Application 對象被實例化。