登錄
小豬快跑22
關注
一個實例讓你徹底明白Activity的4種啟動模式
原創 2017年08月03日 14:46:54 閱讀 792
首先,我們得有一個概念,就是啟動的Activity都是放在相應的任務棧中。按Back鍵的時候Activity會從任務棧中返回,當任務棧為空時系統就會回收這個任務棧。
那么我們為什么需要這4中啟動模式呢?我們新建Activity的并在Androidmanifest.xml文件中注冊的時候,默認的就是standard模式,如果你在這個Activity中一直通過startActivity來啟動這個Activity,那么任務棧中就會有許多該Activity,你要多次按返回鍵才能返回到launcher頁面。這樣的體驗簡直糟糕啊。
模式1:standard
標準模式,也是Activity默認的啟動方式。這個在Androidmanifest.xml中的Activity里聲明,如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="standard" />
1
2
3
以這種方式啟動的Activity每次都會創建一個新的實例,不管這個實例是否已經存在。
比如在 Activity A 中啟動 A,那么任務棧中會有2個A。
例子1:ActivityA 中以standard啟動 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
可以看到 點擊跳轉到 ActivityA 的時候,任然會執行 onCreate方法。
通過指令:adb shell dumpsys activity 查看任務棧:
? 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
可以看出任務棧中有2個ActivityA 。
模式2:singleTop 棧頂復用模式
在這種模式下啟動的Activity,如果該Activity已經位于棧頂,那么就不會去創建新的實例,調用該Activity的onNewIntent方法。如果不在棧頂還是會新建一個Activity實例。
例子: A 啟動B,B的啟動模式為singleTop ,然后 B 再啟動 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跳轉到B時,由于棧中沒有B,所以會新建B的實例并放入棧中;從B跳到B中,由于棧頂以經是B了,所以不會新建實例,而是會調用B的onNewIntent方法。
我們看看任務棧中是不是只有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
可以看到任務棧中只有A和B,也可以看出如果如果B在棧頂就不會創建新的實例。接下來我們看看如果B不是棧頂,那又會如何?
接下來的例子是這樣的 A 啟動 B, B 啟動 C, C 再啟動 B:
代碼很簡單就不貼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再次啟動B的時候B會創建新的實例。我們查看下任務棧看看是什么樣的,任務棧中應該是 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個例子驗證了,以singleTop啟動的Activity,如果棧頂是該Activity,那么不會創建新的實例,如果該Activity不在棧頂,要啟動這個Activity還是會創建新的實例。
模式3:singleTask 站內復用模式
在這種模式下,如果Activity存在于棧中,那么啟動這個Activity不會創建新的實例,而是會調用onNewIntent(),并且將該Activity上面的所有Activity移出棧;當以singleTask 啟動一個Activity的時候,首先去判斷是否要為該Activity去創建一個任務棧?怎么判斷的我們下面再講,如果需要的話,那么就會創建一個任務棧,并且將該Activity放入棧中;如果不需要的話,直接將該Activity放入當前的任務棧中。
現在我們來講怎么判斷是否需要創建任務棧?任務棧的創建跟taskAffinity的屬性相關,每個Activity都有taskAffinity屬性,這個屬性指出了它希望進入的Task。如果一個Activity沒有顯式的指明該Activity的taskAffinity,那么它的這個屬性就等于Application指明的taskAffinity,如果Application也沒有指明,那么該taskAffinity的值就等于包名。
在singleTask 模式下,如果你在要啟動的Activity中設置taskAffinity屬性,且該屬性的值不等于包名,那么就需要創建任務棧,否則不需要創建。taskAffinity的屬性設置如下:
<activity
? ? ? ? ? ? android:name="luanchmode.ActivityB"
? ? ? ? ? ? android:launchMode="singleTask"
? ? ? ? ? ? android:taskAffinity="com.example.aidlclient.test" />
1
2
3
4
而我的應用的包名是:package=”com.example.aidlclient”>
例子1:A中啟動B,B中啟動C,然后C啟動D,D再啟動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 : 由于棧中沒有B且未對B設置android:taskAffinity屬性,所以直接創建B的實例并放在該棧中;
B->C->D:創建C和D的實例并放到棧中
D->B : 由于B已經存在棧中,所以直接將B移到棧頂,調用onNewIntent(),并且B上面的Activity全部出棧。
任務棧如下:
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都在同一個棧中,且從D跳轉到B的時候,B上面的Activity全部出棧。
例子2:為B設置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不存在與任務棧中,所以會新建B的實例,至于有沒有創建對應的棧,log中看不出,等下通過adb來查看。
B->C->D:正常啟動,創建C和D的實例并放入任務棧
D->B:由于B存在于任務棧中,所以直接將B移到棧頂,調用onNewIntent(),并且B上面的Activity全部出棧。
利用 adb shell dumpsys activity查看任務棧情況:
先看 A->B->C->D:的任務棧詳情:
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在一個任務棧,A在另外一個任務棧。
先看 A->B->C->D->B:的任務棧詳情
? 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后,會將B上面的Activity全部移除出棧。
模式4:singleInstance
這種模式下,Activity只能單獨的存在一個任務棧中,舉個例子,就是說A以singleInstance 啟動了B,那么會創建一個任務棧,并把B放到創建的任務棧中,該棧只能有B的存在。
例子1:A啟動B,B啟動C,其中A、C的啟動模式為standard,D的啟動模式為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需要的任務棧,所以先創建任務棧,然后將B壓入棧
B->C:正常啟動,創建C的實例并入棧。
C->B:不會創建B新的實例,而是調用onNewIntent()。
先看 A->B->C的任務棧詳情:
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 會新建一個任務棧然后創建B的實例并壓入該棧;B->C 直接創建C的實例并壓入之前A所在的棧。
Activity的4種啟動模式就寫到這里,下面會寫Intent中設置flag,以及taskAffinity。
閱讀全文
舉報
小豬快跑22訪問量 6萬+ 原創 54 博主更多文章>
Android中Activity的使用,簡單實例講解
qq_32175491 10449次閱讀 2016-12-27 16:09:12
activity例子
shiyanming1223 8317次閱讀 2015-02-28 22:11:30
通過官網的一個例子來看Activity的分組管理
chuyouyinghe 302次閱讀 2016-05-20 10:51:25
如何獲取Activity的實例
u011663865 1394次閱讀 2016-04-02 22:21:38
徹底弄懂Activity四大啟動模式
mynameishuangshuai 70645次閱讀 2016-05-24 16:04:24
Activity的四種啟動模式和應用場景
wangxueming 5020次閱讀 2017-06-15 17:56:44
activity的四種啟動模式區別_launchmode圖文詳解
androidstar_cn 1409次閱讀 2016-10-03 23:55:06
[Android開發]Activity的四種啟動模式及其應用場景
CodeEmperor 26320次閱讀 2016-01-08 11:39:41
Activity的4種啟動模式
wl1fy 74次閱讀 2017-03-28 18:08:42
更多相關文章
CSDN
CSDN精彩內容推薦
查看熊掌號