一、啟動模式簡介
啟動模式相當于Activity的一個屬性攒砖,不同的啟動模式Activity會有不同的行為表現(xiàn)践宴,這里的行為主要體現(xiàn)在Activity的生命周期陨囊,特別是系統(tǒng)在啟動多個Activity實例的時候,具體差異我們將用實例來說明剧辐。
二寒亥、任務(wù)棧簡介
要了解Activity的啟動模式,就不可避免地涉及到Activity所需的任務(wù)棧荧关。什么是任務(wù)棧呢护盈?android系統(tǒng)每啟動一個新的Activity,都要將該Activity實例放入特定的任務(wù)棧羞酗,而這個任務(wù)棧和一個參數(shù)TaskAffinity有關(guān),這個產(chǎn)生標識了Activity所需的任務(wù)棧的名字腐宋,默認的值為應(yīng)用的包名。值得注意的是檀轨,TaskAffinity屬性主要和singleTask啟動模式(下面將要介紹)或allowTaskReparenting屬性配合使用胸竞,在其他情況下沒有意義。
??既然叫“棽翁眩”卫枝,那么就符合“后進先出”的特點,我們每按一下back鍵讹挎,就有一個Activity實例出棧校赤。
三吆玖、Activity啟動模式
1、standard
標準模式马篮,這是系統(tǒng)的默認模式沾乘。每次啟動一個Activity都會重新創(chuàng)建一個Activity實例,不管這個Activity的實例是否存在浑测。
2翅阵、singleTop
棧頂復用模式。在這種模式下迁央,如果有Activity實例位于任務(wù)棧頂掷匠,那么就不會重新創(chuàng)建Activity實例,同時岖圈,Activity的onNewIntent方法會被回調(diào)讹语。
3、singleTask
棧內(nèi)復用模式蜂科。類似于單例模式募强,只要任務(wù)棧中有一個此Activity的實例,那么重新啟動Activity就不會創(chuàng)建新的實例崇摄,而且和singleTop一樣擎值,onNewIntent方法會被回調(diào)
4、singleInstance
單實例模式逐抑,這是一種加強的singleTask模式鸠儿,只要有一個任務(wù)棧中有Activity的實例,那么新實例就不會被創(chuàng)建厕氨。換句話說进每,singleInstance的Activity實例只能位于一個任務(wù)棧中。
下面我將用運行實例代碼結(jié)合日志輸出來分析各種啟動模式的區(qū)別
代碼:
LaunchMode應(yīng)用:
package com.android.yanghuaan.launchmode;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class AActivity extends AppCompatActivity {
private static final String TAG = "Activity_A";
@Override
protected void onRestart() {
Log.d(TAG, "restarted.");
super.onRestart();
}
@Override
protected void onStart() {
Log.d(TAG, "started.");
super.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "created.");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
findViewById(R.id.start_B_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), BActivity.class);
startActivity(intent);
}
});
findViewById(R.id.start_A_self_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), AActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onNewIntent(Intent intent) {
Log.d(TAG, "new intent");
super.onNewIntent(intent);
}
@Override
protected void onResume() {
Log.d(TAG, "resumed.");
super.onResume();
}
@Override
protected void onDestroy() {
Log.d(TAG, "destroyed.");
super.onDestroy();
}
}
package com.android.yanghuaan.launchmode;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class BActivity extends AppCompatActivity {
private static final String TAG = "Activity_B";
@Override
protected void onRestart() {
Log.d(TAG, "restarted.");
super.onRestart();
}
@Override
protected void onStart() {
Log.d(TAG, "started.");
super.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "created.");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
findViewById(R.id.start_A_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), AActivity.class);
startActivity(intent);
}
});
findViewById(R.id.start_B_self_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), BActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onResume() {
Log.d(TAG, "resumed.");
super.onResume();
}
@Override
protected void onDestroy() {
Log.d(TAG, "destroyed.");
super.onDestroy();
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.yanghuaan.launchmode">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--主要在activity中改變啟動模式命斧,不同啟動模式下此處代碼不同-->
<activity
android:name=".AActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".BActivity">
</activity>
</application>
</manifest>
LaunchMode應(yīng)用包含兩個Activity--A和B田晚,每個Activity都有兩個按鈕,一個用來啟動自己国葬,另一個用來啟動另一個Activity贤徒。
LaunchMode2代碼:
package com.android.yanghuaan.launchmode2;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start_other_activity_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClassName("com.android.yanghuaan.launchmode",
"com.android.yanghuaan.launchmode.AActivity");
startActivity(intent);
}
});
}
}
LaunchMode2應(yīng)用包含一個Activity,它可以用來啟動LaunchMode應(yīng)用的一個Activity汇四。只有在singleInstance的例子中才使用到LaunchMode2應(yīng)用接奈,因此寫好只需跑一次、將LaunchMode2安裝到安卓手機上即可通孽。
通過在Activity的生命周期方法中添加輸出日志的代碼來追蹤每個Activity的活動
standard 模式
<activity
android:name=".AActivity"
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=".BActivity">
</activity>
操作步驟:
??進入LaunchMode應(yīng)用后點擊兩次啟動自己的按鈕
日志輸出:
06-13 20:25:07.982 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:25:08.201 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:25:08.201 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:25:11.399 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:25:11.422 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:25:11.423 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:25:14.589 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:25:14.614 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:25:14.614 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed.
分析:
??從日志可以看出序宦,onCreate->onStart->onResume被被調(diào)用了3次,說明創(chuàng)建了3個AActivity的實例背苦,即Activity實例被重復創(chuàng)建了互捌。此時需要3次按back才能返回桌面
singleTop 模式
<activity
android:name=".AActivity"
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=".BActivity"
android:launchMode="singleTop">
</activity>
操作步驟:
??進入LaunchMode應(yīng)用后先按啟動BActivity的按鈕潘明,此時進入了BActivity;再按啟動AActivity的按鈕秕噪,此時進入了AActivity钳降;再按兩次啟動自己(AActivity)的按鈕
日志輸出:
06-13 20:35:07.446 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:35:07.483 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:35:07.483 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:35:10.828 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: created.
06-13 20:35:10.860 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: started.
06-13 20:35:10.860 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: resumed.
06-13 20:35:12.033 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:35:12.068 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:35:12.068 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:35:16.913 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: new intent
06-13 20:35:16.913 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:35:19.872 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: new intent
06-13 20:35:19.873 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: restarted.
06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: started.
06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: resumed.
06-13 20:35:24.394 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: destroyed.
06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: restarted.
06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:35:25.252 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: destroyed.
06-13 20:35:27.292 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: destroyed.
分析:
??從AActivity到BActivity,再啟動AActivity巢价,發(fā)現(xiàn)onCreate被調(diào)用,即表明AActivity實例被重新創(chuàng)建固阁;根據(jù)棧的特點可知此時AActivity的實例并沒有位于棧頂壤躲,所以AActivity實例被重新創(chuàng)建;
??返回AActivity后备燃,再次啟動兩次AActivity都發(fā)現(xiàn)只有onResume和onNewIntent被調(diào)用碉克,可知AActivity的實例被重新使用,并沒有創(chuàng)建新的實例并齐,這兩次創(chuàng)建Activity的過程中AActivity的實例都位于棧頂漏麦,所以沒有重新創(chuàng)建。
??此時連續(xù)按back鍵的效果是:BActivity->AActivity->桌面况褪;
singleTask 模式
<activity
android:name=".AActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".BActivity">
</activity>
操作步驟:
??進入LaunchMode應(yīng)用撕贞,按啟動BActivity的按鈕啟動BActivity,再按啟動AActivity的按鈕啟動AActivity
日志輸出:
06-13 20:47:26.429 32534-32534/com.android.yanghuaan.launchmode D/Activity_B: started.
06-13 20:47:26.429 32534-32534/com.android.yanghuaan.launchmode D/Activity_B: resumed.
06-13 20:47:28.458 32534-32534/com.android.yanghuaan.launchmode D/Activity_A: new intent
06-13 20:47:28.458 32534-32534/com.android.yanghuaan.launchmode D/Activity_A: restarted.
06-13 20:47:28.458 32534-32534/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:47:28.459 32534-32534/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:47:28.780 32534-32534/com.android.yanghuaan.launchmode D/Activity_B: destroyed.
分析:
??從BActivity啟動AActivity测垛,發(fā)現(xiàn)onRestart被調(diào)用捏膨,同時onNewIntent也被調(diào)用,表明AActivity的實例并沒有被重新創(chuàng)建食侮;此時只需按一次back鍵就可以返回桌面号涯,因為在從BActivity創(chuàng)建AActivity的過程中BActivity被出棧了。
singleInstance 模式
<activity
android:name=".AActivity"
android:launchMode="singleInstance"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".BActivity">
</activity>
操作步驟:
??先啟動應(yīng)用LuanchMode锯七,再按啟動BActivity的按鈕啟動BActivity链快;然后通過 home鍵返回桌面,啟動LaunchMode2應(yīng)用眉尸,在里面按啟動其他應(yīng)用Activity的按鈕啟動AActivity域蜗;最后通過 home鍵返回桌面,啟動LaunchMode應(yīng)用
日志輸出:
06-13 20:55:40.044 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: created.
06-13 20:55:40.136 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:55:40.137 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:55:41.046 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: created.
06-13 20:55:41.079 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: started.
06-13 20:55:41.079 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: resumed.
06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: new intent
06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: restarted.
06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed.
06-13 20:56:00.336 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: new intent
06-13 20:56:00.336 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: restarted.
06-13 20:56:00.337 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started.
06-13 20:56:00.337 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed.
分析:
??從LaunchMode2啟動AActivity噪猾,發(fā)現(xiàn)AActivity并沒有被重新創(chuàng)建地消,而是調(diào)用了onNewIntent和onRestart,并且重新從桌面進入LaunchMode應(yīng)用也是如此畏妖,看起來就像AActivity從一個任務(wù)棧中移動到另一個任務(wù)棧中脉执,這充分說明了singleInstance模式的Activity只能存在于一個任務(wù)棧中。