在學習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