在AndroidManifest.xml中配置activity時,android:launchMode屬性會指定啟動activity的模式,有四種:
standard
singleTop
singleTask
singleInstance
這四種模式一般配合Intent屬性變量FLAG_ACTIVITY_XXX使用,比如FLAG_ACTIVITY_NEW_TASK,本文暫時撇開FLAG_ACTIVITY_XXX,只討論這四種模式的啟動結果,先考慮只在同一個應用下的情況。
standard模式
系統默認情況下就是standard模式,假如A中設置為默認模式,A中有一個按鈕,單擊按鈕時只啟動自己,看看啟動后的結果。AndroidManifest.xml、A和源碼及布局分別為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.example.administrator.myapplication.AActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.feeyan.www.a_activity"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
</application>
</manifest>
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class AActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a_layout);
}
public void startToB(View view) {
startActivity(new Intent("com.feeyan.www.a_activity"));
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.administrator.myapplication.AActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="to B"
android:id="@+id/button"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:textSize="20sp"
android:onClick="startToB" />
</RelativeLayout>
|
注:本文案例在小米4上測試
假設現在沒有運行程序,先通過命令dumpsys activity activities在串口中查看所有activity棧信息:
看紅色區域,activity棧中只有一個代號為0的棧,棧中也只有一個id號為#1的task和id號為#0的ActivityRecord,ActivityRecord中保存的包名是com.miui.home,啟動類名為.launcher.Launcher,也就是說,目前Activity棧中只有Launcher應用,沒有別的應用。
現在啟動案例,啟動后的棧信息:
案例啟動后,系統新建了一個棧stack #1,其中有新啟動的Activity實例AActivity,保存在id號為#24的task中
然后,點擊按鈕再次啟動AActivity,啟動后的棧信息為:
在同樣的stack #1、同樣的task #24中多了一個AActicity實例,兩個實例對應的ActivityRecord地址不一樣,一個是42e23ae0,一個是42da3a08。
默認標准模式,每啟動一次,就創建一個新實例,並放到棧頂,並且該實例放在同樣的任務task中、同樣的activity棧中。不會再新創建棧、task。
singleTop模式
如果把A設為默認Standard標准模式,把B設置singleTop模式,A啟動B后看看什么情況,相關源碼較簡單,不再添加,本文后又源碼下載地址。
在stack #1中,新創建了一個task #25,包含B實例和A實例,新啟動的實例位於棧頂。如果在B中點擊按鈕再次啟動B,發現棧信息不會改變,而且無論點擊多少次按鈕,stack #1棧中還是B和A,不會重新創建實例。
假設在A中啟動B,B啟動C,C設為標准模式,啟動后的棧信息:
棧頂是C,其次是B和A,A在棧最底部;如果此時在C中點擊按鈕再啟動B呢?啟動后的棧信息:
發現棧頂又創建了一個B實例,不會復用棧中已有的實例,棧中總共有2個B實例!
singleTop模式跟標准Standard模式差不多,只不過多了一種情況,分為兩種情況來看:
1. 如果棧中已經有了待啟動activity實例並且位於棧頂,那么再次啟動該activity時,系統會直接復用該實例,不會再創建新的實例;如果沒有,就新創建實例,並放到棧頂;
2. 如果棧中已經有了待啟動activity實例,但不在棧頂,那么再次啟動該activity時,系統會再次創建新的實例並將該實例放到棧頂,這種情況和standard模式一樣。
singleTask模式
如果A和C設置standard模式,B設為singleTask模式,A啟動B,B啟動C,按照這個順序啟動后,棧中自底向上為:A—–B—–C,如果此時在C中點擊按鈕再次啟動B呢?啟動后棧信息為:
此時,Task #30中只有A和B,B位於棧頂,沒有C了,這個和singleTop有了明顯的差別,在singleTop中C不會消失,而此時C卻消失了。
1. 如果棧中已經有了待啟動activity實例並且位於棧頂,那么再次啟動該activity時,系統會直接復用該實例,不會再創建新的實例;如果沒有,就新創建實例,並放到棧頂;
2. 如果棧中已經有了待啟動activity實例但不在棧頂,那么再次啟動該activity時,系統會復用已有實例,並且把位於該實例之上的所有其他activity實例移出棧,同時將該實例放在棧頂。
singleInstance模式
如果設A和C為standard模式,B為singleInstance模式,A啟動B后棧信息為:
發現和上面三種情況都不一樣的地方,在棧中新創建了一個task,並把B放入其中,上面三種情況都是在同樣的task中。如果再B中啟動C,啟動后的棧:
A和C都是標准模式,都在同一個task中,而B在另外一個task中,因為啟動了C,C在棧頂。如果再C中點擊按鈕再次啟動B,結果會是什么?
B所在的task跑到了棧頂,A和C所在的task在棧底。而且B始終只有一個實例。
1. 如果棧中沒有待啟動activity實例,啟動該activity時,系統會新創建一個task,再創建一個待啟動activity實例,把該實例放到新task中,並且該task會在棧頂;
2. 如果棧中已經有了待啟動activity實例,不管在棧的什么位置,系統都會復用已存在實例,並且把該實例放在棧頂,而且該task中只有一個該實例,不會再有第二個實例,也不會有其他activity實例;
3. 如果一個應用中有多個activity都設置成singleInstance模式,那么每個啟動后的activity實例都保存在一個task中,不會在同一個task中,task和實例是一對一關系。
同一應用中測試小結
a. standard、singleTop存在多種實例的可能(“可能”二字表明,singleTop情況下,如果棧頂已有實例,再次啟動時只會復用,如果不在棧頂,就會新創建實例);而singleTask和singleInstance只有一個實例,再次啟動時不會創建新實例;
b. singleTask和singleInstance模式,再次調用時都會先調用onNewIntent方法,再調用onResume方法;對於singleTop,如果棧頂已有實例,也是先調用onNewIntent方法,再調用onResume方法,如果不在棧頂或者還沒有實例,就會先調用onCreate方法;
c. singleInstance模式不同於其他三種,首次啟動時會新開啟一個task,該task只包含一個實例;再次啟動時只會復用該task,不再新創建。
同一應用中只有1個app,源碼地址:https://yunpan.cn/crybfHCWhcbwW 訪問密碼:e7cb
不同應用之間測試小結
上面分析了在同一應用中的情況,再看看不同應用之間的情形。通過測試得知:standard、singleTop沒有什么改變,還是在在同樣的棧、task中;singleInstance模式下也沒有改變,還是會創建新的task並保存唯一實例;但singleTask卻不一樣,首次啟動時,系統會在當前棧中創建一個新的task,再次啟動時,復用已有task;而在同一應用中,再次啟動時,不會再創建新task的,直接復用已有task。
不同應用之間測試有2個app,源碼地址:https://yunpan.cn/crKLt2CuZ7drc 訪問密碼 e4b8
最終總結
不管是同一應用中還是不同應用之間,standard、singleTop、singleInstance各自沒有區別,只有singleTask不一樣,同一應用中不創建新的task,不同應用中有可能會創建新的task。(注:這里用到了“可能”二字,沒有用“一定”,是因為這與taskAffinity屬性有關,如果設置了此屬性,就會在該屬性對應的task中啟動實例,否則,會創建新的task)。
轉自:feeyan