Android任務棧機制詳解

語言: CN / TW / HK

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第22天

首先,我們知道,android有任務棧機制,啟動任務棧的方式有xml中配置,還有就是通過intent設定flag去啟動相應的activity。這裡我們分三部分來介紹 1.xml配置的幾種方式詳解 2.intent flag啟動的幾種方式詳解 3.flag 與 xml 幾種方式的區別和聯絡

1.XML配置的幾種方式詳解

Android開發者在AndroidMainifest檔案中一共設計了四種啟動模式,如下所示

1.1 standard

預設的啟動模式,如果不指定Activity的啟動模式,則使用這種方式啟動Activity。這種啟動模式每次都會建立新的例項,每次點選standard模式建立Activity後,都會建立新的MainActivity覆蓋在原Activity上。 實際程式碼驗證 我們例項程式碼驗證一下,新建三個activity,xml中不配置任務棧方式(預設為標準棧),三個activity相互跳轉,程式碼如下: ```java package com.itbird.task;

import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

import com.itbird.R; import com.itbird.bitmapOOM.BitmapUtils; import com.itbird.viewEvent.MyView;

public class MainTestActivity extends AppCompatActivity {

private static final String TAG = MainTestActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.task_test);
    Button button = findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainTestActivity.this, SecondTestActivity.class);
            startActivity(intent);
        }
    });
}

@Override
protected void onNewIntent(Intent intent) {
    Log.e(TAG, TAG + " onNewIntent");
    super.onNewIntent(intent);
}

@Override
protected void onDestroy() {
    Log.e(TAG, TAG + " onDestroy");
    super.onDestroy();
}

} `` 執行之後,跳轉順序為1-2-3-1,通過adb命令檢視後臺任務棧資訊adb shell dumpsys activity activities | grep com.itbird` image.png

我們看到後臺任務棧,的確是保持為同一個,而且activity例項一直是新建累加的。

1.2 singleTop

如果指定啟動Activity為singleTop模式,那麼在啟動時,系統會判斷當前棧頂Activity是不是要啟動的Activity,如果是則不建立新的的Activity而直接引用這個Activity;如果不是則建立新的Activity。 實際程式碼驗證 我們在第一個樣例的基礎程式碼上,把第三個activity任務棧模式改為singleTop,相同的跳轉邏輯,1-2-3-3,執行檢視一下任務棧資訊 大家可以發現,儘管跳轉了兩次3,但是棧中只有一個例項.png

1.3 singleTask

singliTask模式與singleTop模式類似,只不過singleTop是堅持棧頂元素是否是需要啟動的Activity,而singleTask是檢測整個Activity棧中是否存在需要啟動的Activity。如果存在,則將該Activity置於棧頂,並將該Activity以上的Activity都銷燬。不過這裡是指在同一個App中啟動這個singleTask的Activity,如果是其他程式以singleTask模式來啟動這個Activity,那麼它將建立一個新的任務棧。不過這裡有一點需要注意的是,如果啟動的模式為singleTask的Activity已經在後臺一個任務棧中了,那麼啟動後,後臺的這個任務棧將一起被切換到前臺。 實際程式碼驗證 我們在第一個樣例的基礎程式碼上,把第一個activity任務棧模式改為singleTask,相同的跳轉邏輯,1-2-3-1,執行檢視一下任務棧資訊 大家可以發現,任務棧中經過三次跳轉,只剩一個了.png

1.4 singleInstance

singleInstance這種模式和使用閱覽器工資類似。在多個系統訪問閱覽器時,如果當前閱覽器沒有開啟,則開啟閱覽器,否則會在當前開啟的閱覽器中訪問。申明為singleInstance的Activity會出現在一個新的任務棧中,而且這個任務棧只會存在一個Activity。 實際程式碼驗證 我們在第一個樣例的基礎程式碼上,把第三個activity任務棧模式改為singleInstance,相同的跳轉邏輯,1-2-3-3,執行檢視一下任務棧資訊 大家可以發現,首先3是放在了新的棧中,其次跳轉了兩次,但是3的例項只有一個.png

2,Intent flag啟動的幾種方式詳解

2.1 單獨使用

  • FLAG_ACTIVITY_NEW_TASK 加上 FLAG_ACTIVITY_NEW_TASK flag 後,啟動一個 Activity 時,如果需要建立,被啟動的 Activity 會在它需要的棧中建立。如 A 啟動 B,B 會在 B 自己需要的棧中被建立(即 taskAffinity 指定的棧)。

我們還是以之前的三個介面的例子,三個介面的xml配置啟動棧方式都為標準棧,跳轉順序為1-2-3-1,跳轉過程中使用FLAG_ACTIVITY_NEW_TASK flag,程式碼: Intent intent = new Intent(MainTestActivity.this, SecondTestActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 可以看到,在一個任務棧中,即使重新啟動1,也是新建例項1.png 可以看到,在一個任務棧中,即使重新啟動1,也是新建例項1,也就是說,這個Flag只是去找尋親和屬性的棧或者新建棧,並不對棧內的例項進行復用、清空等操作。這裡看出來了吧,FLAG_ACTIVITY_NEW_TASK != singleTask哦,網上有很多誤人子弟的文章,大家還是自己實踐出真知吧

  • FLAG_ACTIVITY_CLEAR_TOP 我們還是以之前的三個介面的例子,三個介面的xml配置啟動棧方式都為標準棧,跳轉順序為1-2-3-1,跳轉過程中使用FLAG_ACTIVITY_CLEAR_TOP flag,程式碼: Intent intent = new Intent(MainTestActivity.this, SecondTestActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); 好神奇,只剩餘一個例項1了.png 這時肯定有小夥伴問了,這不就和singleTask一樣的效果了嗎?我很確定的告訴您,不是哦~~~,clear_top如果檢測到棧中有例項存在,會清空棧內包含本身的以上所有activity,我們看一下activity 1的log日誌列印就知道了 看到了嗎,例項1過程中被銷燬再重建了.png 所以,這裡我們知道一個關鍵點:這裡看出來了吧,FLAG_ACTIVITY_CLEAR_TOP != singleTask哦,網上有很多誤人子弟的文章,大家還是自己實踐出真知吧

  • FLAG_ACTIVITY_SINGLE_TOP 我們還是以之前的三個介面的例子,三個介面的xml配置啟動棧方式都為標準棧,跳轉順序為1-2-3-3,跳轉過程中使用FLAG_ACTIVITY_SINGLE_TOP flag,程式碼: Intent intent = new Intent(MainTestActivity.this, SecondTestActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent); 儘管跳轉了兩次3,但是隻有一個例項.png 從這裡可以看出,FLAG_ACTIVITY_SINGLE_TOP == singleTop哦

2.2 組合使用

針對於組合使用,我們直接說結論吧,依然是上面的例子,很好驗證 - FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_SINGLE_TOP activity 在前臺(當前顯示的棧的棧頂),不建立,它自己 onNewIntent 被呼叫;activity 不在前臺的情形下,activity 需要的棧存在,在該棧中建立一個 activity 或將該棧移至前臺(它自己 onNewIntent 被呼叫),activity 需要的棧不存在,建立一個它需要的棧,再建立 activity

  • FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP activity 在它需要的棧裡存在,它上面的 activity 會出棧,它自己 finish 後重建 activity 在它需要的棧裡不存在,在它需要的棧裡建立 所以此處備註:FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP != singleTask

  • FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_SINGLE_TOP activity 在前臺(當前顯示的棧的棧頂),不建立,它自己 onNewIntent 被呼叫;activity 不在前臺的情形下,activity 在它需要的棧裡存在,它上面的 activity 會出棧,它自己 onNewIntent 被呼叫,activity 在它需要的棧裡不存在,在它需要的棧裡建立。 所以此處備註:FLAG_ACTIVITY_SINGLE_TOP + FLAG_ACTIVITY_CLEAR_TOP == singleTask

3.flag 與 xml 幾種方式的區別和聯絡

其實相關區別和聯絡在上面已經基本都說過了,這裡用等式簡單總結一下: FLAG_ACTIVITY_NEW_TASK != singleTask FLAG_ACTIVITY_CLEAR_TOP != singleTask FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP != singleTask FLAG_ACTIVITY_SINGLE_TOP + FLAG_ACTIVITY_CLEAR_TOP == singleTask FLAG_ACTIVITY_SINGLE_TOP == singleTop 當然這些結論都是基於最初的activity是標準棧的前提,如果將activity最初xml配置改為其他啟動棧模式,結論可能有些就發生了一點點的改變。 我們不需要死記硬背這些知識,這些知識點最好各位結合自己的實踐Demo驗證一波,同時記住一點,FLAG_ACTIVITY_NEW_TASK 只是針對於棧的描述,FLAG_ACTIVITY_CLEAR_TOP 會銷燬本身,這就行了。