Activity的四種啓動模式解析

任務棧

我們都知道,每啓動一個Activity時,系統會將其壓入到一個任務棧中,當我們使用back鍵時,被返回的Activity會從任務棧中彈出,直到棧空爲止。當任務棧中沒有Activity時,系統會回收掉這個任務棧。
然而什麼是任務棧呢?
默認情況下,所有Activity所需的任務棧的名字爲應用的包名
參數:TaskAffinity(任務相關性) :標識了一個Activity所需要的任務棧的名字。 此屬性主要和singleTask配對使用。

任務棧有兩種:

* 前臺任務棧
* 後臺任務棧 處於暫停狀態

四種啓動模式:

standard 標準模式(默認的模式)

每次啓動一個Activity都會重新創建一個新的實例,不管這個實例是否存在。一個任務棧可以有多個實例,每個實例可以屬於不同的任務棧。

例子
現在已啓動MainActivity(用Main表示),那麼系統會將該Activity壓入棧中。如果在MainActivity中要啓動MainActivity,結果會怎樣呢?我們看如下代碼

<!-- launchMode屬性設置Activity爲標準模式 -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="standard" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

我們在MainActivity中啓動MainActivity。代碼如下

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    Button btn_launch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate()");

        btn_launch = (Button) findViewById(R.id.btn_launch);
        btn_launch.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
    }
}

運行結果爲:
這裏寫圖片描述

我們看到,MainActivity執行了兩次onCreate()方法 。表明不管MainActivity是否在任務棧棧頂,啓動MainActivity都會重新創建一次。

singleTop 棧頂複用模式

如果新Activity位於任務棧的棧頂,那麼此Activity不會被重新創建,系統會調用其onNewIntent()。如果新Activity沒有位於任務棧的棧頂,那麼此Activity會被重新創建並位於任務棧棧頂。
例子:
如果任務棧中有MainActivity和SecondActivity,SecondActivity位於棧頂,且SecondActivity的啓動模式爲singleTop。那麼,現在需要啓動SecondActivity,系統是否會重新創建SecondActivity呢?答案是:SecondActivity不會重新創建。
代碼如下

<!-- launchMode屬性設置Activity爲標準模式 -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="standard" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- launchMode屬性設置Activity爲singleTop啓動模式 -->
        <activity
            android:name=".SecondActivity"
            android:label="@string/title_activity_second"
            android:launchMode="singleTop"
             >
        </activity>

MainActivity的代碼

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    Button btn_launch_second;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate()");

        btn_launch_second = (Button) findViewById(R.id.btn_launch_second);
        btn_launch_second.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }

SecondActivity代碼

public class SecondActivity extends Activity {
    private static final String TAG = "SecondActivity";
    Button btn_launch_third;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Log.d(TAG, "onCreate()");
        btn_launch_third = (Button) findViewById(R.id.btn_launch_third);

        btn_launch_third.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }

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

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");
    }

}

輸出結果爲:
這裏寫圖片描述
當再次啓動SecondActivity時,系統不會重新創建Activity實例,而是調用它的onNewIntent()方法。

現在,我們考慮另外一種情況。如果MainActivity爲singleTop啓動模式,而SecondActivity爲標準模式,且位於任務棧棧頂。當我們需要在SecondActivity中啓動MainActivity時,會出現什麼情況?答案是:系統會重新創建MainActivity。
只需將上面的代碼中MainActivity中的啓動模式改爲singleTop,並在SecondActivity中啓動MainActivity即可。這裏就不貼代碼了。
我們看以下運行結果:
這裏寫圖片描述
MainActivity重新創建了。

singleTask 棧內複用模式

這是一種單例模式,只要Activity在一個棧中存在,那麼多次啓動此Activity都不會重新創建。當一個具有singleTask模式的Activity請求啓動後,比如SecondActivity。系統會首先檢查是否存在SecondActivity所需要的任務棧,如果不存在,系統會先創建任務棧,再創建該Activity實例,並將其壓入到該任務棧中。如果存在,系統會檢查SecondActivity是否在任務棧中,如果不在任務棧中,則創建SecondActivity,如果在任務棧中,則不創建,並且將SecondActivity調到棧頂,調用它的onNewIntent()方法。
例子
如果MainActivity爲標準模式,SecondActivity爲singleTask模式,且位於任務棧com.nxiangbo.task01的棧底,而ThirdActivity爲singleTask模式,且位於任務棧com.nxiangbo.task01的棧頂。如果要在ThirdActivity中啓動SecondActivity,那麼,SecondActivity是否需要重新創建?很明顯,答案是:不需要重新創建SecondActivity。因爲SecondActivity已經處於任務棧中,系統會彈出ThirdActivity,讓SecondActivity處於棧頂。
代碼如下

 <!-- launchMode屬性設置Activity爲標準模式 -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- taskAffinity屬性可以標識任務棧的名字 -->
        <!-- launchMode屬性設置Activity的啓動模式 -->
        <activity
            android:name=".SecondActivity"
            android:label="@string/title_activity_second"
            android:taskAffinity="com.nxiangbo.task01"
            android:launchMode="singleTask"
             >
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:taskAffinity="com.nxiangbo.task01"
            android:launchMode="singleTask"
            android:label="@string/title_activity_third" >
        </activity>

MainActivity代碼

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    Button btn_launch_second;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate()");

        btn_launch_second = (Button) findViewById(R.id.btn_launch_second);
        btn_launch_second.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }

}

SecondActivity代碼

public class SecondActivity extends Activity {
    private static final String TAG = "SecondActivity";
    Button btn_launch_third;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Log.d(TAG, "onCreate()");
        btn_launch_third = (Button) findViewById(R.id.btn_launch_third);

        btn_launch_third.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
                startActivity(intent);
            }
        });
    }

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

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");
    }

}

ThirdActivity代碼

public class ThirdActivity extends Activity {
    private static final String TAG = "ThirdActivity";
    Button btn_launch_main;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);
        Log.d(TAG, "onCreate()");
        btn_launch_main = (Button) findViewById(R.id.btn_launch_main);
        btn_launch_main.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ThirdActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

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

}

輸出結果爲:

這裏寫圖片描述

singleInstance 單實例模式

這是一種加強的singleTask,除了具有singleTask的特性外,此種模式只能單獨的位於一個棧中。

舉例:

  • 如果A是singleInstance,啓動A後,系統會爲它創建一個新的任務棧。

我們通過閱讀前面的內容,瞭解到可以在AndroidMenifest.xml中的Activity標籤中 通過launchMode屬性設置Activity的啓動模式。其實,我們也可以用Intent設置標誌位 來設置Activity的啓動模式。

//將Activity設置爲singleTask啓動模式
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

兩者之間的區別爲

* 第二種的優先級高於第一種。
* 第一種不能爲Activity設定FLAG_ACTIVITY_CLEAR_TOP標籤 ;第二種無法爲Activity指定singleInstance

adb shell dumpsys activity 檢測Activity的任務棧

常用Activity的Flags

* FLAG_ACTIVITY_NEW_TASK ==singleTop
* FLAG_ACTIVITY_CLEAR_TOP  ==singleTask
* FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 有此標記的Activity不會出現在歷史Activity列表中

源碼下載地址

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