作項目到如今都一直沒有理解LaunchMode有什麼用,或許根本就沒真正花心思去看,因此今天把這部分整理下。html
設置Activity的LaunchMode屬性能夠決定這個Activity是和當前Task保持關聯,仍是說每次運行這個Activity是新建一個實例,仍是保持單例。java
Task和Back Stack簡介android
task是一組Activities的集合,一組Activities被Stack(back stack)所管理。瀏覽器
在一個應用中,有3個activities,分別是activity1,activity2,activity3,首先activity1被start,此時,若是應用沒有建立task則建立,並把activity1壓入棧頂,activity1觸發onCreate->onStart->onResume。
接着activity1轉向到activity2時,activity1先觸發onPause,activity2觸發onCreate->onStart->onResume,而後activity1觸發onPause->onStop,activity2壓入棧頂。app
以此類推,activity2轉向activity3也是同樣的步驟。那麼當前棧頂是activity3。ide
當咱們按下手機上的返回鍵時,棧頂的activity3觸發onPause,activity2須要從狀態stop到pause,因此觸發了onPause->onStart->onResume,activity3觸發onStop->onDestory,由於activity3從棧頂彈出,因此觸發onDestory,此時,activity2在棧頂。ui
若是繼續按返回鍵,當前棧頂的activity彈出並被destory,直到home界面。當全部的activity都彈出了,這個task也就消亡了。this
當開始一個新的task時,前一個task被設置爲後臺,在後臺,全部的activity都處理stop狀態,可是back stack保留了全部後臺activity的狀態信息,只是丟失了焦點。3d
反覆的在兩個activity之間切換,activity會產生多個獨立的實例。日誌
查閱有關Activity生命週期更多說明。
兩種方式設置LaunchMode屬性
1. 在 manifest文件中設置
<activity android:name=".activity.ActivityA" android:launchMode="standard"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
2. 使用Intent flags設置
Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(ActivityA.this, ActivityB.class); startActivity(intent);
四種LaunchMode說明
standard
不作任何設置,默認模式就是standard,activity在每次start時,都會有一個新的實例被task管理。下面看下代碼實例。
//ActivityA.java Intent intent = new Intent(); intent.setClass(ActivityA.this, ActivityB.class); startActivity(intent); //ActivityB.java Intent intent = new Intent(); intent.setClass(ActivityB.this, ActivityA.class); startActivity(intent);
操做1:在ActivityA(藍)和ActivityB(綠)之間重複切換,按返回鍵推到home界面。
能夠發現(藍色86和綠色79的taskID)ActivityA和ActivityB都在同一個task,而且每次resume的實例都是不同的。這說明在一個activity能夠有多個實例在同一個task中。
在按返回按鍵時,將依次彈出stack。
singleTop
和standard同樣,能夠屢次實例,但,若是處於當前棧頂而且接受到一個與當前activity同樣類型的intent,那麼不會建立一個新實例,而是觸發onNewIntent()事件。
//ActivityA.java Intent intent = new Intent(); intent.setClass(ActivityA.this, ActivityA.class); startActivity(intent); @Override protected void onNewIntent(Intent intent) { logger.d("onNewIntent " + this.hashCode() + " taskID " + this.getTaskId()); super.onNewIntent(intent); }
<activity android:name=".activity.ActivityA" android:label="ActivityA"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
操做1:點擊ActivityA上的按鈕
發現當點擊按鈕是ActivityA->onPause->onNewIntent->onResume,沒有新建新的實例(藍62)。
這個模式在這個場景下比較有用,好比:若是有一個其餘的應用想啓動你的Activity(launch mode爲singleTop),而你當前的Activity正好在棧頂,那麼就會調用到onNewIntent方法。原文貼上:If an instance of the activity already exists at the top of the current task, the system routes the intent to that instance through a call to its onNewIntent()
method。
singleTask
系統會建立一個新task(若是沒有啓動應用)和一個activity新實例在新task根部,而後,若是activity實例已經存在單獨的task中,系統會調用已經存在activity的 onNewIntent()
方法,而不是存在新實例,僅有一個activity實例同時存在。
<activity android:name=".activity.ActivityA" android:label="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=".activity.ActivityB" android:label="ActivityB" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityC" android:label="ActivityC" android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
操做1:ActivityA->ActivityB->ActivityC->ActivityA->ActivityB->ActivityC
能夠看到,當再次進入ActivityB時,沒有onCreate,而是onNewIntent(綠55)。
這裏咱們也能夠發現一個現象,當在調用到ActivityB的onNewIntent時,以前的ActivityA和ActivityC都調用了onDestory。也就是說,系統發現棧中存在ActivityB的實例時,ActivityA和ActivityB都彈棧了。
列出Log日誌(這裏設ActivityA的LaunchMode爲singleTask),ActivityB和ActivityC都在onNewIntent先後調用了onDestory。
singleInstance
和singleTask類似,除了系統不會讓其餘的activities運行在全部持有的task實例中,這個activity是獨立的,而且task中的成員只有它,任何其餘activities運行這個activity都將打開一個獨立的task。
<activity android:name=".activity.ActivityA" 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=".activity.ActivityB" android:launchMode="singleInstance"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </activity> <activity android:name=".activity.ActivityC"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </activity>
操做1:ActivityA->ActivityB->ActivityA
能夠發現,兩個Activity是在不一樣的Task中,其次,當調用到onNewIntent時,ActivityB沒有被Destory,互不干涉。
操做2:ActivityA->ActivityB->ActivityC,按返回鍵
圖解:
剛進入應用,建立TaskA,ActivityA爲棧頂,從ActivityA到ActivityB,ActivityB進入TaskB(若是再次進入ActivityB,則不建立Task,調用onNewIntent),此時TaskB中的ActivityB爲棧頂,從ActitivyB到ActivityC,ActivityC爲棧頂。
一直按返回鍵,先從TaskA中依次將Activity彈出,而後再從TaskB中將ActiviyB彈出。ActiviyC->ActivityA->ActivityB。
這裏分析一個問題,瀏覽器的LaunchMode爲singleTask,因此若是當你點擊一個鏈接下載文件時(由一個activity來處理下載,launchmode爲standard),若是再次進入瀏覽器,那麼下載頁面就被Destory了,那麼這裏咱們能夠把下載頁面LaunchMode設置爲singleInstance能夠解決這個問題。
Affinity更像是代表了activity屬於哪一個task,默認狀況下,應用全部的activities都有相同的affinity,因此都是在相同的task中。而後你能夠編輯默認的affinity。Activities定義在不一樣的應用能夠共享一個affinity,或者activities定義在相同的應用中能夠被不一樣的affinities所關聯。
你能夠編輯在<activity>元素中
activity的taskAffinity屬性。
先看看兩種不一樣的狀況下affinity的表現:
FLAG_ACTIVITY_NEW_TASK標記
//ActivityA.java
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(ActivityA.this, ActivityB.class);
startActivity(intent);
<activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
操做1:不一樣的affinity值,ActivityA->ActivityB
若是已經存在相同affinity,那麼新activity運行在這個task中,不然,系統建立新task。
操做2:相同的affinity值,ActivityA->ActivityB
<activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
能夠看出ActivityA和ActivityB都運行在同一個task中。
- 當Activity的
allowTaskReparenting的屬性設爲'true'
使用來表示是否容許activity從新附屬其餘Task,仍是舉例說明吧。
有兩個應用,Demo1和Demo2,Demo1中有2個Activity(ActivityA,ActivityC),ActivityA能夠轉向到ActivityC,Demo2中有一個Activity(ActivityB),也能夠轉向到ActivityC。
操做1:設置ActivityC的allowTaskReparenting屬性爲true。
運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。
//Demo1
//ActivityA.java
Intent intent = new Intent();
intent.setClass(ActivityA.this, ActivityC.class);
startActivity(intent);
//ActivityC.java
tv.setText(ActivityC.this.toString());
//Demo2
//ActivityB.java
Intent intent = new Intent();
intent.setClassName("com.android.demo","com.android.demo.activity.ActivityC");
ActivityB.this.startActivity(intent);
//Demo1
<activity android:name=".activity.ActivityA">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityC" android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
//Demo2
<activity android:name=".ActivityB">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
運行結果:(黃色Demo1,綠色Demo2)
ActivityB轉向到ActivityC,此時ActivityC就關聯到Demo2的Task中,TaskID都爲231。在運行Demo1時,看到是ActivityC而不是ActivityA。當再次進入Demo2時就看不到ActivityC了。
操做2:將ActivityC的taskAffinity設置爲"com.android.demo.activityc"。
運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。
//Demo1
<activity android:name=".activity.ActivityC"
android:taskAffinity="com.android.demo.activityc"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
運行結果:
從結果中能夠看出,Demo1和Demo2都擁有ActivityC,也就是說有2個Task裏存在ActivityC,分別被Demo1和Demo2所使用。
操做3:將ActivityC和ActivityB的taskAffinity都設爲"com.android.demo.activityc"。
運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。
//Demo2
<activity android:name=".ActivityB" android:taskAffinity="com.android.demo.activityc">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//Demo1
<activity android:name=".activity.ActivityC"
android:taskAffinity="com.android.demo.activityc"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
運行結果:
和操做1相反,再進入Demo2時看到是ActivityC,進入Demo1都是看到ActivityA。
寫到最後愈來愈崩潰了,若是有什麼地方寫的不對或不清楚請指明。
轉帖請說明原文出處:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html