Activity的LaunchMode和taskAffinity全解
做項(xiàng)目到現(xiàn)在都一直沒有理解LaunchMode有什么用,或許根本就沒真正花心思去看,所以今天把這部分整理下。
設(shè)置Activity的LaunchMode屬性可以決定這個(gè)Activity是和當(dāng)前Task保持關(guān)聯(lián),還是說每次運(yùn)行這個(gè)Activity是新建一個(gè)實(shí)例,還是保持單例。
Task和Back Stack簡(jiǎn)介
task是一組Activities的集合,一組Activities被Stack(back stack)所管理。
在一個(gè)應(yīng)用中,有3個(gè)activities,分別是activity1,activity2,activity3,首先activity1被start,此時(shí),如果應(yīng)用沒有創(chuàng)建task則創(chuàng)建,并把a(bǔ)ctivity1壓入棧頂,activity1觸發(fā)onCreate->onStart->onResume。
接著activity1轉(zhuǎn)向到activity2時(shí),activity1先觸發(fā)onPause,activity2觸發(fā)onCreate->onStart->onResume,然后activity1觸發(fā)onPause->onStop,activity2壓入棧頂。
以此類推,activity2轉(zhuǎn)向activity3也是一樣的步驟。那么當(dāng)前棧頂是activity3。
當(dāng)我們按下手機(jī)上的返回鍵時(shí),棧頂?shù)腶ctivity3觸發(fā)onPause,activity2需要從狀態(tài)stop到pause,所以觸發(fā)了onPause->onStart->onResume,activity3觸發(fā)onStop->onDestory,因?yàn)閍ctivity3從棧頂彈出,所以觸發(fā)onDestory,此時(shí),activity2在棧頂。
如果繼續(xù)按返回鍵,當(dāng)前棧頂?shù)腶ctivity彈出并被destory,直到home界面。當(dāng)所有的activity都彈出了,這個(gè)task也就消亡了。
當(dāng)開始一個(gè)新的task時(shí),前一個(gè)task被設(shè)置為后臺(tái),在后臺(tái),所有的activity都處理stop狀態(tài),但是back stack保留了所有后臺(tái)activity的狀態(tài)信息,只是丟失了焦點(diǎn)。
反復(fù)的在兩個(gè)activity之間切換,activity會(huì)產(chǎn)生多個(gè)獨(dú)立的實(shí)例。
查閱有關(guān)Activity生命周期更多說明。
兩種方式設(shè)置LaunchMode屬性
1. 在 manifest文件中設(shè)置
2. 使用Intent flags設(shè)置
Intent?intent?=?new?Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(ActivityA.this,?ActivityB.class); startActivity(intent);
四種LaunchMode說明
standard
不做任何設(shè)置,默認(rèn)模式就是standard,activity在每次start時(shí),都會(huì)有一個(gè)新的實(shí)例被task管理。下面看下代碼實(shí)例。
//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(藍(lán))和ActivityB(綠)之間重復(fù)切換,按返回鍵推到home界面。
可以發(fā)現(xiàn)(藍(lán)色86和綠色79的taskID)ActivityA和ActivityB都在同一個(gè)task,并且每次resume的實(shí)例都是不一樣的。這說明在一個(gè)activity可以有多個(gè)實(shí)例在同一個(gè)task中。
在按返回按鍵時(shí),將依次彈出stack。
??singleTop
和standard一樣,可以多次實(shí)例,但,如果處于當(dāng)前棧頂并且接受到一個(gè)與當(dāng)前activity一樣類型的intent,那么不會(huì)創(chuàng)建一個(gè)新實(shí)例,而是觸發(fā)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); }
操作1:點(diǎn)擊ActivityA上的按鈕
發(fā)現(xiàn)當(dāng)點(diǎn)擊按鈕是ActivityA->onPause->onNewIntent->onResume,沒有新建新的實(shí)例(藍(lán)62)。
這個(gè)模式在這個(gè)場(chǎng)景下比較有用,比如:如果有一個(gè)其他的應(yīng)用想啟動(dòng)你的Activity(launch mode為singleTop),而你當(dāng)前的Activity正好在棧頂,那么就會(huì)調(diào)用到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
系統(tǒng)會(huì)創(chuàng)建一個(gè)新task(如果沒有啟動(dòng)應(yīng)用)和一個(gè)activity新實(shí)例在新task根部,然后,如果activity實(shí)例已經(jīng)存在單獨(dú)的task中,系統(tǒng)會(huì)調(diào)用已經(jīng)存在activity的?onNewIntent()
方法,而不是存在新實(shí)例,僅有一個(gè)activity實(shí)例同時(shí)存在。運(yùn)用實(shí)例:瀏覽器主界面。不管跳轉(zhuǎn)多少頁(yè)面,主界面只啟動(dòng)一次,其余都走onNewIntent,并清空主界面上的其他頁(yè)面。
?操作1:ActivityA->ActivityB->ActivityC->ActivityA->ActivityB->ActivityC
? 可以看到,當(dāng)再次進(jìn)入ActivityB時(shí),沒有onCreate,而是onNewIntent(綠55)。
這里我們也可以發(fā)現(xiàn)一個(gè)現(xiàn)象,當(dāng)在調(diào)用到ActivityB的onNewIntent時(shí),之前的ActivityA和ActivityC都調(diào)用了onDestory。也就是說,系統(tǒng)發(fā)現(xiàn)棧中存在ActivityB的實(shí)例時(shí),ActivityA和ActivityB都彈棧了。
列出Log日志(這里設(shè)ActivityA的LaunchMode為singleTask),ActivityB和ActivityC都在onNewIntent前后調(diào)用了onDestory。
singleInstance
和singleTask相似,除了系統(tǒng)不會(huì)讓其他的activities運(yùn)行在所有持有的task實(shí)例中,這個(gè)activity是獨(dú)立的,并且task中的成員只有它,任何其他activities運(yùn)行這個(gè)activity都將打開一個(gè)獨(dú)立的task。實(shí)例運(yùn)用:適合與程序分離的頁(yè)面,如鬧鈴提醒。
操作1:ActivityA->ActivityB->ActivityA
?
可以發(fā)現(xiàn),兩個(gè)Activity是在不同的Task中,其次,當(dāng)調(diào)用到onNewIntent時(shí),ActivityB沒有被Destory,互不干涉。
操作2:ActivityA->ActivityB->ActivityC,按返回鍵
?
圖解:
剛進(jìn)入應(yīng)用,創(chuàng)建TaskA,ActivityA為棧頂,從ActivityA到ActivityB,ActivityB進(jìn)入TaskB(如果再次進(jìn)入ActivityB,則不創(chuàng)建Task,調(diào)用onNewIntent),此時(shí)TaskB中的ActivityB為棧頂,從ActitivyB到ActivityC,ActivityC為棧頂。
一直按返回鍵,先從TaskA中依次將Activity彈出,然后再?gòu)腡askB中將ActiviyB彈出。ActiviyC->ActivityA->ActivityB。
這里分析一個(gè)問題,瀏覽器的LaunchMode為singleTask,所以如果當(dāng)你點(diǎn)擊一個(gè)連接下載文件時(shí)(由一個(gè)activity來(lái)處理下載,launchmode為standard),如果再次進(jìn)入瀏覽器,那么下載頁(yè)面就被Destory了,那么這里我們可以把下載頁(yè)面LaunchMode設(shè)置為singleInstance可以解決這個(gè)問題。
Affinity定義
Affinity更像是表明了activity屬于哪個(gè)task,默認(rèn)情況下,應(yīng)用所有的activities都有相同的affinity,所以都是在相同的task中。然后你可以編輯默認(rèn)的affinity。Activities定義在不同的應(yīng)用可以共享一個(gè)affinity,或者activities定義在相同的應(yīng)用中可以被不同的affinities所關(guān)聯(lián)。
你可以編輯在activity的taskAffinity屬性。
?先看看兩種不同的情況下affinity的表現(xiàn):
當(dāng)運(yùn)行一個(gè)activity包含了FLAG_ACTIVITY_NEW_TASK標(biāo)記
//ActivityA.java Intent?intent?=?new?Intent();???? intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(ActivityA.this,?ActivityB.class); startActivity(intent);
操作1:不同的affinity值,ActivityA->ActivityB
如果已經(jīng)存在相同affinity,那么新activity運(yùn)行在這個(gè)task中,否則,系統(tǒng)創(chuàng)建新task。
操作2:相同的affinity值,ActivityA->ActivityB
可以看出ActivityA和ActivityB都運(yùn)行在同一個(gè)task中。
?
當(dāng)Activity的allowTaskReparenting的屬性設(shè)為'true'
使用來(lái)表示是否允許activity重新附屬其他Task,還是舉例說明吧。
有兩個(gè)應(yīng)用,Demo1和Demo2,Demo1中有2個(gè)Activity(ActivityA,ActivityC),ActivityA可以轉(zhuǎn)向到ActivityC,Demo2中有一個(gè)Activity(ActivityB),也可以轉(zhuǎn)向到ActivityC。
操作1:設(shè)置ActivityC的allowTaskReparenting屬性為true。
運(yùn)行Demo2,轉(zhuǎn)向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運(yùn)行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 //Demo2
?運(yùn)行結(jié)果:(黃色Demo1,綠色Demo2)
? ?ActivityB轉(zhuǎn)向到ActivityC,此時(shí)ActivityC就關(guān)聯(lián)到Demo2的Task中,TaskID都為231。在運(yùn)行Demo1時(shí),看到是ActivityC而不是ActivityA。當(dāng)再次進(jìn)入Demo2時(shí)就看不到ActivityC了。
操作2:將ActivityC的taskAffinity設(shè)置為"com.android.demo.activityc"。
運(yùn)行Demo2,轉(zhuǎn)向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運(yùn)行Demo1。
//Demo1
?運(yùn)行結(jié)果:
? 從結(jié)果中可以看出,Demo1和Demo2都擁有ActivityC,也就是說有2個(gè)Task里存在ActivityC,分別被Demo1和Demo2所使用。
操作3:將ActivityC和ActivityB的taskAffinity都設(shè)為"com.android.demo.activityc"。
運(yùn)行Demo2,轉(zhuǎn)向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運(yùn)行Demo1。
//Demo2 //Demo1
運(yùn)行結(jié)果:
? 和操作1相反,再進(jìn)入Demo2時(shí)看到是ActivityC,進(jìn)入Demo1都是看到ActivityA。
?
寫到最后越來(lái)越崩潰了,如果有什么地方寫的不對(duì)或不清楚請(qǐng)指明。
轉(zhuǎn)帖請(qǐng)說明原文出處:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html?
另一篇 圖文并茂?http://www.androidchina.net/3173.html?
兩種設(shè)置方式:
1) XML
2)在java代碼中設(shè)置intent.setFlags()
Intent?intent?=?new?Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(this,ActivitySub.ckass); startActivity(intent);