登錄
小豬快跑22
關(guān)注
一個(gè)實(shí)例讓你徹底明白Activity的4種啟動(dòng)模式
原創(chuàng) 2017年08月03日 14:46:54 閱讀 792
首先,我們得有一個(gè)概念,就是啟動(dòng)的Activity都是放在相應(yīng)的任務(wù)棧中。按Back鍵的時(shí)候Activity會(huì)從任務(wù)棧中返回,當(dāng)任務(wù)棧為空時(shí)系統(tǒng)就會(huì)回收這個(gè)任務(wù)棧。
那么我們?yōu)槭裁葱枰@4中啟動(dòng)模式呢?我們新建Activity的并在Androidmanifest.xml文件中注冊(cè)的時(shí)候,默認(rèn)的就是standard模式,如果你在這個(gè)Activity中一直通過(guò)startActivity來(lái)啟動(dòng)這個(gè)Activity,那么任務(wù)棧中就會(huì)有許多該Activity,你要多次按返回鍵才能返回到launcher頁(yè)面。這樣的體驗(yàn)簡(jiǎn)直糟糕啊。
模式1:standard
標(biāo)準(zhǔn)模式,也是Activity默認(rèn)的啟動(dòng)方式。這個(gè)在Androidmanifest.xml中的Activity里聲明,如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="standard" />
1
2
3
以這種方式啟動(dòng)的Activity每次都會(huì)創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在。
比如在 Activity A 中啟動(dòng) A,那么任務(wù)棧中會(huì)有2個(gè)A。
例子1:ActivityA 中以standard啟動(dòng) ActivityA,代碼如下:
manefest.xml:
<activity android:name="luanchmode.ActivityA"
? ? ? ? ? ? android:launchMode="standard">
? ? ? ? ? ? <intent-filter>
? ? ? ? ? ? ? ? <action android:name="android.intent.action.MAIN" />
? ? ? ? ? ? ? ? <category android:name="android.intent.category.LAUNCHER" />
? ? ? ? ? ? </intent-filter>
? ? ? ? </activity>
1
2
3
4
5
6
7
8
ActivityA.java:
public class ActivityA extends Activity {
? ? @Override
? ? protected void onCreate(@Nullable Bundle savedInstanceState) {
? ? ? ? Log.e("xxx", "ActivityA, onCreate");
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? TextView tv = new TextView(this);
? ? ? ? tv.setTextSize(20);
? ? ? ? tv.setText("This is activityA");
? ? ? ? tv.setGravity(Gravity.CENTER);
? ? ? ? tv.setBackgroundColor(Color.parseColor("#999999"));
? ? ? ? setContentView(tv, new ViewGroup.LayoutParams(200, 100));
? ? ? ? tv.setFocusable(true);
? ? ? ? tv.requestFocus();
? ? ? ? tv.setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View v) {
? ? ? ? ? ? ? ? Log.e("xxx","onclick A");
? ? ? ? ? ? ? ? Intent intent = new Intent(ActivityA.this, ActivityA.class);
? ? ? ? ? ? ? ? startActivity(intent);
? ? ? ? ? ? }
? ? ? ? });
? ? }
? ? @Override
? ? protected void onNewIntent(Intent intent) {
? ? ? ? Log.e("xxx", "ActivityA, onNewIntent");
? ? ? ? super.onNewIntent(intent);
? ? }
}
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
27
28
29
30
31
32
33
34
35
log如下:
08-02 19:33:45.720 11791-11791/com.example.aidlclient E/xxx: ActivityA, onCreate
08-02 19:33:59.192 11791-11791/com.example.aidlclient E/xxx: onclick A
08-02 19:33:59.207 11791-11791/com.example.aidlclient E/xxx: ActivityA, onCreate
1
2
3
可以看到 點(diǎn)擊跳轉(zhuǎn)到 ActivityA 的時(shí)候,任然會(huì)執(zhí)行 onCreate方法。
通過(guò)指令:adb shell dumpsys activity 查看任務(wù)棧:
? Running activities (most recent first):
? ? TaskRecord{2470eca6 #1587 A=com.example.aidlclient U=0 sz=2}
? ? ? Run #1: ActivityRecord{3456102c u0 com.example.aidlclient/luanchmode.ActivityA t1587}
? ? ? Run #0: ActivityRecord{352831f7 u0 com.example.aidlclient/luanchmode.ActivityA t1587}
1
2
3
4
可以看出任務(wù)棧中有2個(gè)ActivityA 。
模式2:singleTop 棧頂復(fù)用模式
在這種模式下啟動(dòng)的Activity,如果該Activity已經(jīng)位于棧頂,那么就不會(huì)去創(chuàng)建新的實(shí)例,調(diào)用該Activity的onNewIntent方法。如果不在棧頂還是會(huì)新建一個(gè)Activity實(shí)例。
例子: A 啟動(dòng)B,B的啟動(dòng)模式為singleTop ,然后 B 再啟動(dòng) B;
AndroidManifest.xml如下:
<activity android:name="luanchmode.ActivityA"
? ? ? ? ? ? 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="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleTop" />
? ? ? ? <activity
1
2
3
4
5
6
7
8
9
10
11
12
AcitivityA 和 ActivityB 中的部分代碼如下:
tv.setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View v) {
? ? ? ? ? ? ? ? Intent intent = new Intent(ActivityA.this, ActivityB.class);
? ? ? ? ? ? ? ? startActivity(intent);
? ? ? ? ? ? }
? ? ? ? });
1
2
3
4
5
6
7
tv.setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View v) {
? ? ? ? ? ? ? ? Intent intent = new Intent(ActivityB.this, ActivityB.class);
//? ? ? ? ? ? ? ? intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
? ? ? ? ? ? ? ? startActivity(intent);
? ? ? ? ? ? }
? ? ? ? });
1
2
3
4
5
6
7
8
log如下:
08-03 09:13:01.841 26939-26939/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 09:18:08.513 26939-26939/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 09:18:09.983 26939-26939/com.example.aidlclient E/xxx: ActivityB, onNewIntent
1
2
3
可以看出從A跳轉(zhuǎn)到B時(shí),由于棧中沒(méi)有B,所以會(huì)新建B的實(shí)例并放入棧中;從B跳到B中,由于棧頂以經(jīng)是B了,所以不會(huì)新建實(shí)例,而是會(huì)調(diào)用B的onNewIntent方法。
我們看看任務(wù)棧中是不是只有A和B ?
Running activities (most recent first):
? ? TaskRecord{351e6f43 #1596 A=com.example.aidlclient U=0 sz=2}
? ? Run #1: ActivityRecord{2925d326 u0 com.example.aidlclient/luanchmode.ActivityB t1596}
? ? Run #0: ActivityRecord{1e64c90 u0 com.example.aidlclient/luanchmode.ActivityA t1596}
1
2
3
4
可以看到任務(wù)棧中只有A和B,也可以看出如果如果B在棧頂就不會(huì)創(chuàng)建新的實(shí)例。接下來(lái)我們看看如果B不是棧頂,那又會(huì)如何?
接下來(lái)的例子是這樣的 A 啟動(dòng) B, B 啟動(dòng) C, C 再啟動(dòng) B:
代碼很簡(jiǎn)單就不貼A和B的了,貼下C的。
tv.setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View v) {
? ? ? ? ? ? ? ? Intent intent = new Intent(ActivityC.this, ActivityB.class);
//? ? ? ? ? ? ? ? intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
? ? ? ? ? ? ? ? startActivity(intent);
? ? ? ? ? ? }
? ? ? ? });
1
2
3
4
5
6
7
8
log如下:
08-03 09:36:35.259 17108-17108/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 09:36:39.482 17108-17108/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 09:36:40.979 17108-17108/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 09:36:42.171 17108-17108/com.example.aidlclient E/xxx: ActivityB, onCreate
1
2
3
4
可以看出,C再次啟動(dòng)B的時(shí)候B會(huì)創(chuàng)建新的實(shí)例。我們查看下任務(wù)棧看看是什么樣的,任務(wù)棧中應(yīng)該是 A->B->C->B.
Running activities (most recent first):
? ? ? TaskRecord{1d40b112 #1602 A=com.example.aidlclient U=0 sz=4}
? ? ? ? Run #3: ActivityRecord{3482a81a u0 com.example.aidlclient/luanchmode.ActivityB t1602}
? ? ? ? Run #2: ActivityRecord{a101309 u0 com.example.aidlclient/luanchmode.ActivityC t1602}
? ? ? ? Run #1: ActivityRecord{3575d5f8 u0 com.example.aidlclient/luanchmode.ActivityB t1602}
? ? ? ? Run #0: ActivityRecord{31f704f3 u0 com.example.aidlclient/luanchmode.ActivityA t1602}
1
2
3
4
5
6
以上2個(gè)例子驗(yàn)證了,以singleTop啟動(dòng)的Activity,如果棧頂是該Activity,那么不會(huì)創(chuàng)建新的實(shí)例,如果該Activity不在棧頂,要啟動(dòng)這個(gè)Activity還是會(huì)創(chuàng)建新的實(shí)例。
模式3:singleTask 站內(nèi)復(fù)用模式
在這種模式下,如果Activity存在于棧中,那么啟動(dòng)這個(gè)Activity不會(huì)創(chuàng)建新的實(shí)例,而是會(huì)調(diào)用onNewIntent(),并且將該Activity上面的所有Activity移出棧;當(dāng)以singleTask 啟動(dòng)一個(gè)Activity的時(shí)候,首先去判斷是否要為該Activity去創(chuàng)建一個(gè)任務(wù)棧?怎么判斷的我們下面再講,如果需要的話,那么就會(huì)創(chuàng)建一個(gè)任務(wù)棧,并且將該Activity放入棧中;如果不需要的話,直接將該Activity放入當(dāng)前的任務(wù)棧中。
現(xiàn)在我們來(lái)講怎么判斷是否需要?jiǎng)?chuàng)建任務(wù)棧?任務(wù)棧的創(chuàng)建跟taskAffinity的屬性相關(guān),每個(gè)Activity都有taskAffinity屬性,這個(gè)屬性指出了它希望進(jìn)入的Task。如果一個(gè)Activity沒(méi)有顯式的指明該Activity的taskAffinity,那么它的這個(gè)屬性就等于Application指明的taskAffinity,如果Application也沒(méi)有指明,那么該taskAffinity的值就等于包名。
在singleTask 模式下,如果你在要啟動(dòng)的Activity中設(shè)置taskAffinity屬性,且該屬性的值不等于包名,那么就需要?jiǎng)?chuàng)建任務(wù)棧,否則不需要?jiǎng)?chuàng)建。taskAffinity的屬性設(shè)置如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleTask"
? ? ? ? ? ? android:taskAffinity="com.example.aidlclient.test" />
1
2
3
4
而我的應(yīng)用的包名是:package=”com.example.aidlclient”>
例子1:A中啟動(dòng)B,B中啟動(dòng)C,然后C啟動(dòng)D,D再啟動(dòng)B,其中A和C、D是standard模式,B是singleTask模式。
manifest.xml如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityA"
? ? ? ? ? ? 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="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleTask" />
? ? ? ? <activity android:name="luanchmode.ActivityC" />
? ? ? ? <activity android:name="luanchmode.ActivityD" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
還是先看log:
08-03 10:40:33.896 14823-14823/? E/xxx: ActivityA, onCreate
08-03 10:40:42.777 14823-14823/? E/xxx: ActivityB, onCreate
08-03 10:40:44.169 14823-14823/? E/xxx: ActivityC onCreate
08-03 10:40:45.218 14823-14823/? E/xxx: ActivityD onCreate
08-03 10:40:46.696 14823-14823/? E/xxx: ActivityB, onNewIntent
1
2
3
4
5
6
7
A->B : 由于棧中沒(méi)有B且未對(duì)B設(shè)置android:taskAffinity屬性,所以直接創(chuàng)建B的實(shí)例并放在該棧中;
B->C->D:創(chuàng)建C和D的實(shí)例并放到棧中
D->B : 由于B已經(jīng)存在棧中,所以直接將B移到棧頂,調(diào)用onNewIntent(),并且B上面的Activity全部出棧。
任務(wù)棧如下:
Running activities (most recent first):
? ? ? TaskRecord{33b61cae #1611 A=com.example.aidlclient U=0 sz=2}
? ? ? ? Run #1: ActivityRecord{2291853f u0 com.example.aidlclient/luanchmode.ActivityB t1611}
? ? ? ? Run #0: ActivityRecord{390ec8d6 u0 com.example.aidlclient/luanchmode.ActivityA t1611}
1
2
3
4
可以看到A和B都在同一個(gè)棧中,且從D跳轉(zhuǎn)到B的時(shí)候,B上面的Activity全部出棧。
例子2:為B設(shè)置android:taskAffinity=”com.example.aidlclient.test”和包名不一樣
manifest.xml如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleTask"
? ? ? ? ? ? android:taskAffinity="com.example.aidlclient.test"/>
1
2
3
4
log如下:
08-03 10:45:24.335 15566-15566/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 10:45:38.858 15566-15566/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 10:45:40.912 15566-15566/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 10:45:42.694 15566-15566/com.example.aidlclient E/xxx: ActivityD onCreate
08-03 10:45:44.406 15566-15566/com.example.aidlclient E/xxx: ActivityB, onNewIntent
1
2
3
4
5
A->B:由于B不存在與任務(wù)棧中,所以會(huì)新建B的實(shí)例,至于有沒(méi)有創(chuàng)建對(duì)應(yīng)的棧,log中看不出,等下通過(guò)adb來(lái)查看。
B->C->D:正常啟動(dòng),創(chuàng)建C和D的實(shí)例并放入任務(wù)棧
D->B:由于B存在于任務(wù)棧中,所以直接將B移到棧頂,調(diào)用onNewIntent(),并且B上面的Activity全部出棧。
利用 adb shell dumpsys activity查看任務(wù)棧情況:
先看 A->B->C->D:的任務(wù)棧詳情:
Running activities (most recent first):
? ? ? TaskRecord{3874c557 #1618 A=com.example.aidlclient.test U=0 sz=3}
? ? ? ? Run #3: ActivityRecord{84f09e0 u0 com.example.aidlclient/luanchmode.ActivityD t1618}
? ? ? ? Run #2: ActivityRecord{255bda6b u0 com.example.aidlclient/luanchmode.ActivityC t1618}
? ? ? ? Run #1: ActivityRecord{2ac8c0a u0 com.example.aidlclient/luanchmode.ActivityB t1618}
? ? ? TaskRecord{1d0e98b2 #1617 A=com.example.aidlclient U=0 sz=1}
? ? ? ? Run #0: ActivityRecord{3863a8b u0 com.example.aidlclient/luanchmode.ActivityA t1617}
1
2
3
4
5
6
7
可以看出 B、C、D在一個(gè)任務(wù)棧,A在另外一個(gè)任務(wù)棧。
先看 A->B->C->D->B:的任務(wù)棧詳情
? Running activities (most recent first):
? ? ? TaskRecord{3874c557 #1618 A=com.example.aidlclient.test U=0 sz=1}
? ? ? ? Run #1: ActivityRecord{2ac8c0a u0 com.example.aidlclient/luanchmode.ActivityB t1618}
? ? ? TaskRecord{1d0e98b2 #1617 A=com.example.aidlclient U=0 sz=1}
? ? ? ? Run #0: ActivityRecord{3863a8b u0 com.example.aidlclient/luanchmode.ActivityA t1617}
1
2
3
4
5
可以看出,從B->D后,會(huì)將B上面的Activity全部移除出棧。
模式4:singleInstance
這種模式下,Activity只能單獨(dú)的存在一個(gè)任務(wù)棧中,舉個(gè)例子,就是說(shuō)A以singleInstance 啟動(dòng)了B,那么會(huì)創(chuàng)建一個(gè)任務(wù)棧,并把B放到創(chuàng)建的任務(wù)棧中,該棧只能有B的存在。
例子1:A啟動(dòng)B,B啟動(dòng)C,其中A、C的啟動(dòng)模式為standard,D的啟動(dòng)模式為singleInstance 。
manifest.xml如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityA"
? ? ? ? ? ? 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="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleInstance" />
? ? ? ? <activity android:name="luanchmode.ActivityC" />
1
2
3
4
5
6
7
8
9
10
11
12
13
log如下:
08-03 11:07:29.810 5490-5490/com.example.aidlclient E/xxx: ActivityA, onCreate
08-03 11:07:37.698 5490-5490/com.example.aidlclient E/xxx: ActivityB, onCreate
08-03 11:07:41.398 5490-5490/com.example.aidlclient E/xxx: ActivityC onCreate
08-03 11:08:37.934 5490-5490/com.example.aidlclient E/xxx: ActivityB, onNewIntent
1
2
3
4
A->B:由于不存在B需要的任務(wù)棧,所以先創(chuàng)建任務(wù)棧,然后將B壓入棧
B->C:正常啟動(dòng),創(chuàng)建C的實(shí)例并入棧。
C->B:不會(huì)創(chuàng)建B新的實(shí)例,而是調(diào)用onNewIntent()。
先看 A->B->C的任務(wù)棧詳情:
Running activities (most recent first):
? ? ? TaskRecord{2d63becf #1621 A=com.example.aidlclient U=0 sz=2}
? ? ? ? Run #2: ActivityRecord{340fa242 u0 com.example.aidlclient/luanchmode.ActivityC t1621}
? ? ? TaskRecord{323683de #1622 A=com.example.aidlclient U=0 sz=1}
? ? ? ? Run #1: ActivityRecord{2aef961d u0 com.example.aidlclient/luanchmode.ActivityB t1622}
? ? ? TaskRecord{2d63becf #1621 A=com.example.aidlclient U=0 sz=2}
? ? ? ? Run #0: ActivityRecord{3486b42c u0 com.example.aidlclient/luanchmode.ActivityA t1621}
1
2
3
4
5
6
7
A->B 會(huì)新建一個(gè)任務(wù)棧然后創(chuàng)建B的實(shí)例并壓入該棧;B->C 直接創(chuàng)建C的實(shí)例并壓入之前A所在的棧。
Activity的4種啟動(dòng)模式就寫(xiě)到這里,下面會(huì)寫(xiě)Intent中設(shè)置flag,以及taskAffinity。
閱讀全文
舉報(bào)
小豬快跑22訪問(wèn)量 6萬(wàn)+ 原創(chuàng) 54 博主更多文章>
Android中Activity的使用,簡(jiǎn)單實(shí)例講解
qq_32175491 10449次閱讀 2016-12-27 16:09:12
activity例子
shiyanming1223 8317次閱讀 2015-02-28 22:11:30
通過(guò)官網(wǎng)的一個(gè)例子來(lái)看Activity的分組管理
chuyouyinghe 302次閱讀 2016-05-20 10:51:25
如何獲取Activity的實(shí)例
u011663865 1394次閱讀 2016-04-02 22:21:38
徹底弄懂Activity四大啟動(dòng)模式
mynameishuangshuai 70645次閱讀 2016-05-24 16:04:24
Activity的四種啟動(dòng)模式和應(yīng)用場(chǎng)景
wangxueming 5020次閱讀 2017-06-15 17:56:44
activity的四種啟動(dòng)模式區(qū)別_launchmode圖文詳解
androidstar_cn 1409次閱讀 2016-10-03 23:55:06
[Android開(kāi)發(fā)]Activity的四種啟動(dòng)模式及其應(yīng)用場(chǎng)景
CodeEmperor 26320次閱讀 2016-01-08 11:39:41
Activity的4種啟動(dòng)模式
wl1fy 74次閱讀 2017-03-28 18:08:42
更多相關(guān)文章
CSDN
CSDN精彩內(nèi)容推薦
查看熊掌號(hào)