Android 中 Application,Task和Process 關係

介紹

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 這個概念保證了跨應用操作的連貫性。
如下場景:

  1. 應用X 中的某 Activity(X1Activity) 位於 Task(id = 104)
  2. 應用Y 中有個 Activity(Y1Activity) 開發了打開意圖,同過Uri指定就能訪問,設置默認啓動模式
  3. X1Activity 通過 Uri 方式匿名打開 Y1Activity
  4. 此時 Y1Activity 位於 Task(id = 104)中,且在棧的位置在 X1Activity 上部
  5. 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 在同一個棧,打開、回退流程栗子

  1. AppA 的 ActivityA(Task1)
  2. 手機按下 Home 進入主界面
  3. 打開 AppB 默認啓動 ActivityB1(Task2)
  4. ActivityB1 打開 ActivityB2(Task1)(和ActivityA一個棧)
  5. 此時顯示的是 AppB 的 ActivityB2
  6. 按下返回鍵
  7. 此時顯示的是 AppA 的 ActivityA

所以在 Android 系統中按下返回鍵是對當前 Task 棧進行出棧操作,然後顯示此 Task 棧臨近棧頂的元素。

特殊情況

App 默認啓動 ActivityL 設置 taskAffinity,同名 Task 棧在開啓 App 之前已存在。

  1. 爲 standard、singleTop設置時。 會顯示當前 Task 棧頂 Activity,不會創建 ActivityL 的實例(不走生命週期)。
  2. 爲 singleTask 設置時。會創建 ActivityL 實例展示並放在棧頂,按返回鍵顯示棧頂下一個 Activity。
  3. 爲 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 對象被實例化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章