Android開發之Activity的啓動模式

黑髮不知勤學早,白首方悔讀書遲。——《勸學》


    今天花了整個下午+晚上的的時間學習了Activity的啓動模式,本來以爲這個知識點很簡單,但是在學習的過程中發現,Activity的啓動模式並沒有自己想象的那麼簡單,下面我們一起來看看這Activity的四種啓動模式吧,如有疑問歡迎留言,如有謬誤歡迎大家批評指正,謝謝


Activity的啓動模式共有四種

1.standard

2.singleTop

3.singleTask

4.singleInstance

如圖所示:


LaunchMode在多個Activity跳轉的過程中扮演着重要的角色,它可以決定是否生成新的Activity實例,是否重用已存在的Activity實例,是否和其他Activity實例公用一個task裏。這裏簡單介紹一下task的概念,task是一個具有棧結構的對象,一個task可以管理多個Activity,啓動一個應用,也就創建一個與之對應的task。

下面我們就依次來說說這幾種啓動模式

1.standard

standard模式是Activity默認的啓動模式,當我們在沒有配置activity的launchMode時它就會按照standard方式去啓動,

下面通過一個實例來解釋下這種啓動模式

FirstActivity代碼如下:

package com.example.activitylauchmodepractice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class FirstActivity extends Activity {
	
	private Button btn_jumpToSecondActivity;
	private TextView tv_showViewClass;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_first);
		
		tv_showViewClass=(TextView) findViewById(R.id.tv_showViewClass);
		tv_showViewClass.setText(FirstActivity.this.toString());
		
		btn_jumpToSecondActivity=(Button) findViewById(R.id.btn_jumpToSecondActivity);
		btn_jumpToSecondActivity.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent=new Intent(FirstActivity.this,FirstActivity.class);
				startActivity(intent);
			}
		});
	}
	
}
啓動後的界面



此時它所對應的任務棧如下


在此基礎上我們點擊按鈕再次啓動Activity此時的界面如下




此時的任務棧變化過程如下



我們再次點擊按鈕跳轉到FirstActivity界面如下



此時的任務棧的變化過程如下


好了到這我們就可以分析一下了,在上述過程中我們點擊了三次按鈕它實例化了三個FirstActivity

這就是standard模式的特點:不管任務棧中有沒有實例存在它都會實例化一個Activity

當我們點擊返回按鈕時它會依次把最上面的Activity出棧,上面的過程中一共實例化了三個Activity因此我們需要點擊三次返回按鈕應用才能退出。

2.singleTop

還用上面那個例子,此時我們給FirstActivity的屬性指定爲:android:launchMode="singleTop"

啓動後的界面


此時的任務棧如下


我們接着點擊按鈕發現無論點擊幾次界面都沒變說明它只實例化一次,此時的任務站始終是一個Activity此時點擊一次返回鍵便可退出應用。

這是隻有一個Activity的情況,下面我們說說多個Activity的情況

再來一個SecondActivity代碼如下:

package com.example.activitylauchmodepractice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class SecondActivity extends Activity {
	
	private Button btn_jumpToFirstActivity_;
	private TextView tv_showViewClass;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_second);
		
		tv_showViewClass=(TextView) findViewById(R.id.tv_showViewClass);
		tv_showViewClass.setText(SecondActivity.this.toString());
		
		btn_jumpToFirstActivity_=(Button) findViewById(R.id.btn_jumpToFirstActivity);
		btn_jumpToFirstActivity_.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent=new Intent(SecondActivity.this, FirstActivity.class);
				startActivity(intent);
			}
		});
	}
}
把FirstActivity的代碼稍作修改

btn_jumpToSecondActivity.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
				startActivity(intent);
			}
		});
在上面的Activity中FirstActivity的啓動模式是singleTop,SecondActivity的啓動模式是默認的standard,做好準備之後我們來做操作

啓動後的界面如下:

此時的任務棧如下

在此基礎上我們點擊一次按鈕界面如下


此時的任務棧的變化如下


在SecondActivity中再次點擊按鈕的界面如下


此時的任務棧的變化如下

從上面的過程中我們看到再次從SecondActivity跳轉到FirstActivity時兩次的FirstActivity的序列號不同說明又重新生成了一個FirstActivity

singleTop模式的特點:當從SecondActivity跳轉到FirstActivity時,系統發現存在有FirstActivity實例,但不是位於棧頂,於是重新生成一個實例。這就是singleTop啓動模式的特點,即如果發現有對應的Activity實例正位於棧頂,則重複利用,不再生成新的實例,如果棧頂沒有對應的Activity則實例化一個。

該模式和standard模式基本一致,但有一點不同,當將要被啓動的Activity已經位於Task棧頂時,系統不會重新創建目標Activity的實例,而是直接複用Task棧頂的Activity

3.singleTask(內單例模式)

我們還是建立在上面的基礎上,把FirstActivity的啓動模式改爲android:launchMode="singleTask"

啓動後我們點擊三次跳轉按鈕界面如下圖所示



在上面的過程中,FirstActivity的序列號是不變的,SecondActivity的序列號是改變的,說明從SecondActivity跳轉到FirstActivity時,沒有生成新的實例,但是從FirstActivity跳轉到SecondActivity時生成了新的實例。

在此過程中任務棧的變化過程如下


在上面的跳轉過程中當從SecondActivity跳轉到FirstActivity時發現SecondActivity消失了,這就是singleTask的特點在這個跳轉過程中系統發現有存在的FirstActivity實例,於是不再生成新的實例,而是將FirstActivity之上的Activity實例統統出棧,將FirstActivity變爲棧頂對象,顯示到幕前。

singleTask模式的特點:如果發現有對應的Activity實例,則使此Activity實例之上的其他Activity實例統統出棧,使此Activity實例成爲棧頂對象,顯示到幕前。

Activity在同一個Task內只有一個實例. 當系統採用singleTask模式加載Activity時,又分爲以下三種情況:

(1)如果將要啓動的Activity不存在,那麼系統將會創建該實例,並將其加入Task棧頂

(2)如果將要啓動的Activity已存在,且存在棧頂,那麼此時與singleTop模式的行爲相同

(3)如果將要啓動的Activity存在但是沒有位於棧頂,那麼     此時系統會把位於該Activity上面的所有其他Activity全部移出Task,從而使得該目標Activity位於棧頂


4.singleInstance(全局單例模式)

這種模式是四種模式中最難理解的一種模式,因爲這種模式會重新創建一個新的任務棧,將Activity放置於這個棧中,並保證其它的Activity不再進入,由於這種模式比較複雜,我們首先來說說它的原理,然後再結合實例進一步的理解,假如現在用戶打開了兩個應用分別爲應用1和應用2,應用1和應用2的任務棧假如如下圖左邊,此時在應用1中想打開Activity3,這時應用1和應用2就會共享Activity3的引用,

注意:之所以能公用Activity的引用是以應用2中的Activity設置了LaunchMode="singleInstance"爲前提的。


由於這種模式比較複雜,我們舉兩個不同例子,來說明不同的問題

舉例一、

還是上面的兩個Activity。FirstActivity和SecondActivity在兩個Activityt跳轉的過程中我們打印兩個Activity所在的任務棧的ID

對以上兩個Activity做如下修改,並且把SecondActivity的啓動模式改爲singleInstance

tv_showViewClass=(TextView) findViewById(R.id.tv_showViewClass);
tv_showViewClass.setText("當前Activity:"+"\n"+this.toString()+"\n"+"當前TaskId:"+this.getTaskId());
啓動後和點擊跳轉按鈕後的界面如下


我們發現兩個Activity的TaskId是不同的,說明這兩個Activity是位於不同的任務棧中的,從而證實了爲SecondActivity重新建立了一個任務棧,可能有的朋友會問,在這個時候如果點擊返回按鈕它們是怎麼出棧的呢?假如現在我們點擊返回按鈕它的任務棧的變化如下圖


假如我們在SecondActivity中點擊按鈕跳轉到FirstActivity然後會以怎樣的方式退出應用呢?此時 它的任務棧的變化如下



圖中下半部分顯示的在SecondActivity中再次跳轉到FirstActivity,這個時候系統會在原始棧結構中生成一個FirstActivity實例,然後回退兩次,注意,並沒有退出,而是回到了SecondActivity,爲什麼呢?是因爲從SecondActivity跳轉到FirstActivity的時候,我們的起點變成了SecondActivity實例所在的棧結構,這樣一來,我們需要“迴歸”到這個棧結構。

由於singleInstance比較複雜些,我們再來舉一個兩個應用的例子爲了和上面的例子混淆,我們重新寫兩個應用

第一個App中有兩個Activity分別爲Activity1和ShareActivity

第二個App中有一個Activity2我們在這個App中啓動第一個App的ShareActivity

第一個App的Activity源碼如下

package com.example.activitylauchmodepractice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class Activity1 extends Activity {
	
	private Button btn_jumpToSecondActivity;
	private TextView tv_showViewClass;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_first);
		
		tv_showViewClass=(TextView) findViewById(R.id.tv_showViewClass);
		tv_showViewClass.setText("當前Activity:"+"\n"+this.toString()+"\n"+"當前TaskId:"+this.getTaskId());
		
		btn_jumpToSecondActivity=(Button) findViewById(R.id.btn_jumpToSharedActivity);
		btn_jumpToSecondActivity.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent=new Intent(Activity1.this,ShareActivity.class);
				startActivity(intent);
			}
		});
	}
	
}
ShareActivity源碼

package com.example.activitylauchmodepractice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ShareActivity extends Activity {
	
	private Button btn_jump;
	private TextView tv_showViewClass;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_second);
		
		tv_showViewClass=(TextView) findViewById(R.id.tv_showViewClass);
		tv_showViewClass.setText("當前Activity:"+"\n"+this.toString()+"\n"+"當前TaskId:"+this.getTaskId());
		
	}
}

我們要特別注意ShareActivity在清單文件中的配置如下

<activity 
     android:name="com.example.activitylauchmodepractice.ShareActivity"
     android:launchMode="singleInstance">
        <intent-filter>
             <action android:name="SecondActivity_action"/>
             <category android:name="android.intent.category.DEFAULT"/>
          </intent-filter>
            
</activity>

我們需要配置ShareActivity的action在另一個應用中啓動時會用到
第二個App中的Activity2的源碼如下

package com.example.singleinstancepractice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	
	private Button btn_jump;
	private TextView tv_showTaskId;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv_showTaskId=(TextView) findViewById(R.id.tv_showTaskId);
		tv_showTaskId.setText("當前Activity:"+"\n"+this.toString()+"\n"+"當前TaskId:"+this.getTaskId());
		btn_jump=(Button) findViewById(R.id.btn_jump);
		btn_jump.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent =new Intent();
				intent.setAction("SecondActivity_action");
				startActivity(intent);
			}
		});
	}
}
當我們在第一個App中打開ShareActivity後再按後退鍵回到原來界面時,ShareActivity做爲一個獨立的個體存在,如果這時我們在第二個App中打開ShareActivity無需創建新的ShareActivity實例即可看到結果,因爲系統會自動查找,存在則直接利用。原理圖如下:

注意:上圖是建立在第一個App運行到手機上時點擊第二個App上的跳轉按鈕跳轉到ShareActivity的情況的基礎上的變化過程。









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