Activity的四種啓動模式如下:
standard、singleTop、singleTask、singleInstance
我們一邊講理論一邊結合案例來全面學習這四種啓動模式。
爲了打印方便,定義一個基礎BaseActivity,在其onCreate方法和onNewIntent方法中打印出當前Activity的日誌信息,主要包括所屬的task,當前類的hashcode,之後我們進行測試的Activity都直接繼承該BaseActivity
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("maweiqi", "*****onCreate()方法******");
Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
dumpTaskAffinity();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i("maweiqi", "*****onNewIntent()方法*****");
Log.i("maweiqi", "onNewIntent:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
dumpTaskAffinity();
}
protected void dumpTaskAffinity(){
try {
ActivityInfo info = this.getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Log.i("maweiqi", "taskAffinity:"+info.taskAffinity);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
standard-默認模式
這個模式是默認的啓動模式,即標準模式,在不指定啓動模式的前提下,系統默認使用該模式啓動Activity,每次啓動一個Activity都會重寫創建一個新的實例,不管這個實例存不存在,這種模式下,誰啓動了該模式的Activity,該Activity就屬於啓動它的Activity的任務棧中。
配置形式:
<activity android:name=".SecondActivity" android:launchMode="standard"/>
使用案例:
對於standard模式,android:launchMode可以不進行聲明,因爲默認就是standard。 SecondActivity的代碼如下,入口MainActivity中有一個按鈕進入該Activity,這個Activity中又有一個按鈕啓動SecondActivity。
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_standard= (Button) findViewById(R.id.btn_standard);
btn_standard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
Log.i("maweiqi", "*****onCreate()方法******");
Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
}
}
public class SecondActivity extends BaseActivity {
private Button jump;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
jump= (Button) findViewById(R.id.button3);
jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SecondActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}
我們首先從MainActivity進入SecondActivity ,進入後再點擊進入SecondActivity,連續進入四次SecondActivity 。
輸出的日誌如下:
MainActivity TaskId: 2 hasCode:1249333352
SecondActivity TaskId: 2 hasCode:1249526392
SecondActivity TaskId: 2 hasCode:1249424816
SecondActivity TaskId: 2 hasCode:1249439692
SecondActivity TaskId: 2 hasCode:1249459968
可以看到日誌輸出了四次SecondActivity的和一次MainActivity的,從MainActivity進入StandardActivity一次,後來我們又按了三次按鈕,總共四次SecondActivity的日誌,並且所屬的任務棧的id都是2,這也驗證了誰啓動了該模式的Activity,該Activity就屬於啓動它的Activity的任務棧中這句話.因爲啓動SecondActivity的是MainActivity,而MainActivity的taskId是2,因此啓動的SecondActivity也應該屬於id爲2的這個task,後續的3個SecondActivity是被SecondActivity這個對象啓動的,因此也應該還是2,所以taskId都是2。並且每一個Activity的hashcode都是不一樣的,說明他們是不同的實例,即“每次啓動一個Activity都會重寫創建一個新的實例”
singleTop模式
這個模式下,如果新的activity已經位於棧頂,那麼這個Activity不會被重新創建,同時它的onNewIntent方法會被調用,通過此方法的參數我們可以去除當前請求的信息。如果棧頂不存在該Activity的實例,則情況與standard模式相同。需要注意的是這個Activity它的onCreate(),onStart()方法不會被調用,因爲它並沒有發生改變。
配置形式:
<activity android:name=".SingleTopActivity" android:launchMode="singleTop"/>
<activity android:name=".OtherTopActivity" android:launchMode="singleTop"/>
使用案例:
public class SingleTopActivity extends BaseActivity {
private Button jump, jump2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singletop);
jump = (Button) findViewById(R.id.button);
jump2 = (Button) findViewById(R.id.button2);
jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTopActivity.this, SingleTopActivity.class);
startActivity(intent);
}
});
jump2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTopActivity.this, OtherTopActivity.class);
startActivity(intent);
}
});
Log.i("maweiqi", "*****onCreate()方法******");
Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
}
}
public class OtherTopActivity extends AppCompatActivity {
private Button jump;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
jump= (Button) findViewById(R.id.btn_other);
jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(OtherTopActivity.this, SingleTopActivity.class);
startActivity(intent);
}
});
Log.i("maweiqi", "*****onCreate()方法******");
Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
}
}
操作和standard模式類似,直接貼輸出日誌
onCreate:MainActivity TaskId: 3 hasCode:1249332216
onCreate:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
我們看到,除了第一次進入SingleTopActivity這個Activity時,輸出的是onCreate方法中的日誌,後續的都是調用了onNewIntent方法,並沒有調用onCreate方法,並且四個日誌的hashcode都是一樣的,說明棧中只有一個實例。這是因爲第一次進入的時候,棧中沒有該實例,則創建,後續的三次發現棧頂有這個實例,則直接複用,並且調用onNewIntent方法。那麼假設棧中有該實例,但是該實例不在棧頂情況又如何呢?
我們先從MainActivity中進入到SingleTopActivity,然後再跳轉到OtherActivity中,再從OtherActivity中跳回SingleTopActivity,再從SingleTopActivity跳到SingleTopActivity中,看看整個過程的日誌。
輸出的日誌如下:
onCreate:SingleTopActivity TaskId: 4 hasCode:1249520904
onCreate:OtherTopActivity TaskId: 4 hasCode:1249420244
onCreate:SingleTopActivity TaskId: 4 hasCode:1249448776
onCreate:SingleTopActivity TaskId: 4 hasCode:1249448776
onNewIntent:SingleTopActivity TaskId: 4 hasCode:1249448776
我們看到從MainActivity進入到SingleTopActivity時,新建了一個SingleTopActivity對象,然後從SingleTopActivity跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到SingleTopActivity,即使棧中已經有SingleTopActivity實例了,但是依然會創建一個新的SingleTopActivity實例,這一點從上面的日誌的hashCode可以看出,此時棧頂是SingleTopActivity,如果再跳到SingleTopActivity,就會複用棧頂的SingleTopActivity,即會調用SingleTopActivity的onNewIntent方法。這就是上述日誌的全過程。
對以上內容進行總結
standard啓動模式是默認的啓動模式,每次啓動一個Activity都會新建一個實例不管棧中是否已有該Activity的實例。
singleTop模式分3種情況
1)當前棧中已有該Activity的實例並且該實例位於棧頂時,不會新建實例,而是複用棧頂的實例,並且會將Intent對象傳入,回調onNewIntent方法
2)當前棧中已有該Activity的實例但是該實例不在棧頂時,其行爲和standard啓動模式一樣,依然會創建一個新的實例
3)當前棧中不存在該Activity的實例時,其行爲同standard啓動模式standard和singleTop啓動模式都是在原任務棧中新建Activity實例,不會啓動新的Task
singleTask模式
在這個模式下,如果棧中存在這個Activity的實例就會複用這個Activity,不管它是否位於棧頂,複用時,會將它上面的Activity全部出棧,並且會回調該實例的onNewIntent方法。
配置形式:
<activity android:name=".SingleTaskActivity" android:launchMode="singleTask"/>
<activity android:name=".OtherTaskActivity" android:launchMode="singleTask"/>
使用案例:
public class SingleTaskActivity extends BaseActivity {
private Button jump,jump2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_task);
jump = (Button) findViewById(R.id.btn_task);
jump2 = (Button) findViewById(R.id.btn_other);
jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTaskActivity.this, SingleTaskActivity.class);
startActivity(intent);
}
});
jump2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTaskActivity.this, OtherTaskActivity.class);
startActivity(intent);
}
});
}
}
public class OtherTaskActivity extends BaseActivity {
private Button jump;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other_task);
jump= (Button) findViewById(R.id.btn_other);
jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(OtherTaskActivity.this, SingleTaskActivity.class);
startActivity(intent);
}
});
}
}
日誌輸出
onCreate:MainActivity TaskId: 5 hasCode:1249321980
onCreate:SingleTaskActivity TaskId: 5 hasCode:1249515136
onCreate:OtherTaskActivity TaskId: 6 hasCode:1249386172
onNewIntent:SingleTaskActivity TaskId: 6 hasCode:1249513244
當我們從MainActiviyty進入到SingleTaskActivity,再進入到OtherActivity後,此時棧中有3個Activity實例,並且SingleTaskActivity不在棧頂,而在OtherActivity跳到SingleTaskActivity時,並沒有創建一個新的SingleTaskActivity,而是複用了該實例,並且回調了onNewIntent方法。並且原來的OtherActivity出棧了,具體見下面的信息,使用命令adb shell dumpsys activity activities可進行查看
Running activities (most recent first):
TaskRecord{3c727e #11 A=com.maweiqi.task U=0 sz=2}
Run #1: ActivityRecord{5a00d1e u0 com.maweiqi.task/.SingleTaskActivity t11}
Run #0: ActivityRecord{2dce0b u0 com.maweiqi.task/.MainActivity t11}
可以看到當前棧中只有兩個Activity,即原來棧中位於SingleTaskActivity 之上的Activity都出棧了。
singleInstance-全局唯一模式
該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全局唯一性,即整個系統中就這麼一個實例,由於棧內複用的特性,後續的請求均不會創建新的Activity實例,除非這個特殊的任務棧被銷燬了。以singleInstance模式啓動的Activity在整個系統中是單例的,如果在啓動這樣的Activiyt時,已經存在了一個實例,那麼會把它所在的任務調度到前臺,重用這個實例。
singleInstance示例一配置形式:
<activity android:name=".MainActivity" android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance"/>
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_standard= (Button) findViewById(R.id.btn_standard);
btn_standard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SingleInstanceActivity.class);
startActivity(intent);
}
});
}
}
public class SingleInstanceActivity extends BaseActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_instance);
Button button4 = (Button) findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(SingleInstanceActivity.this, MainActivity.class);
startActivity(intent);
}
});
}
}
MainActivity進入SingleInstanceActivity,在從SingleInstanceActivity 進入MainActivity,在從MainActivity進入SingleInstanceActivity
日誌輸出
onCreate:MainActivity TaskId: 12 hasCode:201331476
onCreate:SingleInstanceActivity TaskId: 13 hasCode:57987178
onCreate:MainActivity TaskId: 12 hasCode:254253633
onNewIntent:SingleInstanceActivity TaskId: 13 hasCode:57987178
測試結果
1)兩個SingleInstanceActivity是同一個實例。
2) 第一次進入的MainActivity和第一次進入的SingleInstanceActivity位於不同的task中。
3) 兩個MainActivity是位於同一個task中的不同實例。
4)這個結論與預期是相同的,即,singleInstance類型的Activity的實例只能有一個,而且它只允許存在於單獨的一個task中。
5)至於爲什麼兩個MainActivity是位於同一個task中的不同實例,那是因爲它是standard類型的,我們可以將ActivityTest修改爲singleTop等其他類型進行測試。
singleInstance示例二配置形式:
MainActivity的模式改爲”singleTop”,修改後的manifest如下:
<activity android:name=".MainActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance"/>
MainActivity進入SingleInstanceActivity,在從SingleInstanceActivity 進入MainActivity,在從MainActivity進入SingleInstanceActivity
日誌輸出
onCreate:MainActivity TaskId: 15 hasCode:201331476
onCreate:SingleInstanceActivity TaskId: 16 hasCode:57987178
onNewIntent:MainActivity TaskId: 15 hasCode:201331476
onNewIntent:SingleInstanceActivity TaskId: 16 hasCode:57987178
測試結果
1)兩個SingleInstanceActivity是同一個實例。
2)第一次進入的MainActivity和第一次進入的SingleInstanceActivity位於不同的task中。
3)兩個MainActivity是同一個實例。
launchMode模式總結
1. standard
在該模式下,Activity可以擁有多個實例,並且這些實例既可以位於同一個task,也可以位於不同的task。
2.singleTop
該模式下,在同一個task中,如果存在該Activity的實例,並且該Activity實例位於棧頂(即,該Activity位於前端),則調用startActivity()時,不再創建該Activity的示例;而僅僅只是調用Activity的onNewIntent()。否則的話,則新建該Activity的實例,並將其置於棧頂。
3. singleTask
只容許有一個包含該Activity實例的task存在!
總的來說:singleTask的結論與android:taskAffinity相關(下章在講),以A啓動B來說
1) 當A和B的taskAffinity相同時:第一次創建B的實例時,並不會啓動新的task,而是直接將B添加到A所在的task;否則,將B所在task中位於B之上的全部Activity都刪除,然後跳轉到B中。
2) 當A和B的taskAffinity不同時:第一次創建B的實例時,會啓動新的task,然後將B添加到新建的task中;否則,將B所在task中位於B之上的全部Activity都刪除,然後跳轉到B中。
4. singleInstance
顧名思義,是單一實例的意思,即任意時刻只允許存在唯一的Activity實例,而且該Activity所在的task不能容納除該Activity之外的其他Activity實例!
它與singleTask有相同之處,也有不同之處。
相同之處:任意時刻,最多隻允許存在一個實例。
不同之處:
1) singleTask受android:taskAffinity屬性的影響,而singleInstance不受android:taskAffinity的影響。
2) singleTask所在的task中能有其它的Activity,而singleInstance的task中不能有其他Activity。
3) 當跳轉到singleTask類型的Activity,並且該Activity實例已經存在時,會刪除該Activity所在task中位於該Activity之上的全部Activity實例;而跳轉到singleInstance類型的Activity,並且該Activity已經存在時,不需要刪除其他Activity,因爲它所在的task只有該Activity唯一一個Activity實例。
參考鏈接:http://blog.csdn.net/sbsujjbcy/article/details/49360615
歡迎關注微信公衆號,長期推薦技術文章和技術視頻
微信公衆號名稱:Android乾貨程序員