Android開發中Activity的啓動模式

在學習Activity的啓動模式時,也是分不清楚他們各自在返回棧中的運作機制,網上也有很多文章講解了,現在我就將我學習過程中對Activity的啓動模式的理解寫出來,大家一起學習。
在實際的項目開發中,我們應該爲不同的活動指定不同的啓動模式。啓動模式一共有四種,分別是standard、singleTop、singleTask、singleInstance。可以在AndroidManifest.xml中通過<activity>標籤來指定android:launchMode 屬性來選擇啓動模式。

在講解Activity的啓動模式之前,先來說說返回棧
Android是使用Task(任務)來管理活動的,一個任務就是一組存放在棧裏的活動集,這樣的棧就是返回棧(Back Stack)。棧是一種先進後出的數據結構(學習編程這點肯定要知道的,數據結構這門知識很必要)。在默認的情況下我們每啓動一個新的活動,它就會在返回棧中入棧,並且處於棧頂的位置。同樣每當我們按“返回鍵”或者調用finish( )方法時候,處於棧頂的活動就會出棧(先進先出),這個時候前一個入棧的活動就會處於棧頂位置(若此時棧內沒有活動,程序就會退出)。程序顯示給我們看到的總是處於棧頂位置的活動。

standard

standard是活動的默認啓動模式。如果在AndroidManifest.xml中沒有進行指定,那麼所有活動都會自動使用這種啓動模式。

現在我們創建一個Activity命名爲FirstActivity,並且在活動中調用一個設有按鈕的layout頁面(first_activity_layout.xml),代碼如下:
FirstActivity頁面的onCreate( )方法代碼:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity",this.toString());  
        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

first_activity_layout.xml頁面代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="FirstActivity"/>
</LinearLayout>

因爲standard是Activity的默認啓動模式,因此就可以不再AndroidManifest.xml中進行指定
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);看起來比較奇怪,在FirstActivity頁面上啓動FirstActivity,但是我們是研究standard的啓動模式。

但是沒關係,我們在代碼中添加了日誌打印代碼Log.d("FirstActivity",this.toString());,用來打印當前活動的實例。

運行程序後當我們點擊按鈕幾次後,會發現在日誌中也打印了幾次。從日誌中我們看出,每次點擊一次按鈕就會創建一個新的FirstActivity實例,所以在返回棧中就存在多個FirstActivity實例,因此,當我們想要退出程序時就需要按返回鍵多次。

singleTop

singleTop模式:當活動的啓動模式指定爲singleTop,在啓動活動時如果發現棧頂已經是該活動時,則直接使用該活動,不再創建新的實例。

1、當FirstActivity處於棧頂

首先在AndroidManifest.xml中將FirstActivity的啓動模式修改下:

 <activity android:name=".FirstActivity"
              android:launchMode="singleTop"
              android:label="This is a FirstActivity">
             <!-- 定義當前活動的啓動模式爲singleTop,默認情況下是standard-->
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>

因爲FirstActivity爲該程序的主活動,所以在AndroidManifest.xml有主活動的聲明。
在運行程序時,在下面的日誌文件中會發現打印一次FirstActivity活動實例,當多次點擊按鈕時,就不在創建實例。因爲FirstActivity的啓動模式爲singleTop,啓動活動時發現棧頂已經是該活動,就會直接使用,並不創建。

2、當FirstActivity不在棧頂

首先修改FirstActivity中的onCreate ( )方法,使其點擊按鈕跳轉到SecondActivity活動中。

FirstActivity活動的onCreate( )方法的代碼:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity",this.toString());
        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

然後在SecondActivity的 second_activity_layout中設置按鈕跳轉到FirstActivity中。(實現兩個活動中相互跳轉,但是FirstActivity的啓動方式是singleTop)
SecondActivity的onCreate( )方法的代碼:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("SecondActivity",this.toString());
        setContentView(R.layout.second_activity_layout);
        Button button2=(Button)findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(SecondActivity.this,FirstActivity.class);
                startActivity(intent);
            }
        });
    }

second_activity_layout.xml的佈局代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:id="@+id/button_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="SecondActivity"/>
</LinearLayout>

我們運行程序時,在FirstActivity界面點擊按鈕進入到SecondActivity界面中,在SecondActivity界面點擊按鈕,由會重新進入到FirstActivity界面。

com.example.chencong.activitytest_02.SecondActivity@53696bfc
com.example.chencong.activitytest_02.FirstActivity@536a18e8
com.example.chencong.activitytest_02.SecondActivity@5369a110
com.example.chencong.activitytest_02.FirstActivity@536ba028

我們可以看到系統創建了兩個不同的FirstActivity實例,這是由於在SecondActivity界面中再次啓動FirstActivity時,棧頂活動此時是SecondActivity,因此需要啓動singleTop啓動模式的FirstActivity時,系統就會創建一個新的FirstActivity實例。
現在按下“返回鍵”會返回在SecondActivity界面,再次按下“返回鍵”又會回到FirstActivity界面,再按一次“返回鍵”纔會退出程序。

singleTop啓動模式的原理圖:
這裏寫圖片描述

singleTask

當活動的指定模式爲singleTask時,每次啓動該活動時,系統首先在返回棧中檢查是否存在該活動的實例(並不是檢查棧頂),如果發現已經存在則直徑使用實例,並將該活動之上的活動統統出棧(系統要保證顯示給用戶的活動在棧頂,所以要將該活動之上的活動統統出棧),如果沒有發現就重新常見一個新的活動實例。

首先需要在AndroidManifest.xml中修改FirstActivity活動的啓動模式:

 <activity android:name=".FirstActivity"
              android:launchMode="singleTask"
              android:label="This is a FirstActivity">
             <!-- 定義當前活動的啓動模式爲singleTop,默認情況下是standard-->
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>

然後在FirstActivity中添加onRestart( )方法,並打印日誌:

@Override
    protected void onRestart() {
        super.onRestart();
        Log.d("FirstActivity","onRestart");
    }

最後在SecondActivity中添加onRestart( )方法,並且打印日誌:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("SecondActivity","onDestroy");
    }

重新運行程序打印日誌信息:

 D/FirstActivity: com.example.chencong.activitytest_02.FirstActivity@536bb97c
 D/SecondActivity: com.example.chencong.activitytest_02.SecondActivity@536c1cd8
 D/FirstActivity: onRestart
 D/SecondActivity: onDestroy

從上面的日誌信息中顯示,在SecondActivity中啓動FirstActivity時,會發現在返回棧中已經存在一個FirstActivity實例,並且是在SecondActivity下面(此時棧頂是SecondActivity),由於FirstActivity的啓動方式爲singleTask,於是SecondActivity就會出棧,並且FirstActivity就會成爲棧頂活動。
因此FirstActivity活動就會onRestart( )方法就會被調用,而SecondActivity活動的onDestroy( )方法也會執行。並且現在返回棧中只剩下FirstActivity一個活動,當我們按下“返回鍵”時就會推出程序。

singleTask的啓動原理圖:

這裏寫圖片描述

singleInstance

當活動的啓動方式指定爲singleInstance時,當啓動活動時會啓用一個新的返回棧來管理這個活動。
假象下,我們程序中有個返回棧屬於自己活動可以進去的,但是當有別的活動(外部程序的活動)需要訪問我們的活動時,就沒辦法與我們程序的活動在這個返回棧內,那就需要重新創建一個 返回棧來管理這種模式的活動。

現在我們在上面幾種方式基礎下面,創建活動FirstActivity、SecondActivity、ThirdActivity。活動SecondActivity的啓動模式設置爲singleInstance。

首先在AndroidManifest.xml中修改SecondActivity的啓動模式:

 <activity android:name=".SecondActivity"
            android:launchMode="singleInstance">
 </activity>

然後修改FirstActivity活動中的 onCreate( )方法中的代碼:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity","Task id is"+getTaskId());

        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

在返回棧中打印日誌,打印當前返回棧中的id。

然後修改SecondActivity活動中的onCreate( )方法:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        Log.d("SecondActivity",this.toString());
        Log.d("SecondActivity", "Task id is " + getTaskId());
        setContentView(R.layout.second_activity_layout);
        Button button2=(Button)findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(SecondActivity.this,FirstActivity.class);
                startActivity(intent);
            }
        });
    }

最後在ThirdActivity活動中修改onCreate( )方法:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("ThirdActivity", "Task id is" + getTaskId());
        setContentView(R.layout.third_activity_layout);
    }

仍然是在日誌中打印當前返回棧中的id;

好了,現在運行程序,在日誌中打印的 當前返回棧的 id信息爲:

D/FirstActivity: Task id is 56
D/SecondActivity: Task id is 57
D/ThirdActivity: Task id is 56

從上面的日誌信息中可以看出,FirstActivity和ThirdActivity是放在相同的返回棧中,但是SecondActivity是放在單獨的返回棧中,並且棧中只有 一個活動。

同樣,當我們按下“返回鍵”時,會發現ThirdActivity界面直接返回到了FirstActivity界面,再次按下“返回鍵”時,返回到SecondActivity界面,當我們再次按下“返回鍵”程序則會退出。

原因:
因爲ThirdActivity和FirstActivity在一個返回棧中,首先ThirdActivity在棧頂,放我們按下“返回鍵”時,ThirdActivity出棧,位於其下面的FirstActivity就到了棧頂,顯示爲用戶看到 ;當再次按下“返回鍵”,FirstActivity出棧後這個棧就爲空了,那麼系統就會顯示另外一個返回棧的棧頂活動,因爲此時SecondActivity位於另外的返回棧的棧頂,所以會在最後顯示SecondActivity。當顯示SecondActivity時再次按下“返回鍵”時,所有返回棧爲空,程序退出。

singleInstance模式的原理圖:

這裏寫圖片描述

啓動模式的原理圖做的不是很好,文章如果有錯誤,希望廣大網友指出,大家一起學習進步。

參考資料:
    1、第一行代碼-Android
                --郭霖
    2、網上一些博客、文章和各大網友解答問題我學習得到的。
我揮舞着鍵盤和本子,
發誓要把世界寫個明明白白。

By 2016年5月27日00:30:22

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