Activity的LaunchMode和taskAffinity
Thanks to:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html?
【原】Activity的LaunchMode和taskAffinity
做項目到現在都一直沒有理解LaunchMode有什么用,或許根本就沒真正花心思去看,所以今天把這部分整理下。
設置Activity的LaunchMode屬性可以決定這個Activity是和當前Task保持關聯,還是說每次運行這個Activity是新建一個實例,還是保持單例。
Task和Back Stack簡介
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壓入棧頂。
以此類推,activity2轉向activity3也是一樣的步驟。那么當前棧頂是activity3。
當我們按下手機上的返回鍵時,棧頂的activity3觸發onPause,activity2需要從狀態stop到pause,所以觸發了onPause->onStart->onResume,activity3觸發onStop->onDestory,因為activity3從棧頂彈出,所以觸發onDestory,此時,activity2在棧頂。
如果繼續按返回鍵,當前棧頂的activity彈出并被destory,直到home界面。當所有的activity都彈出了,這個task也就消亡了。
當開始一個新的task時,前一個task被設置為后臺,在后臺,所有的activity都處理stop狀態,但是back stack保留了所有后臺activity的狀態信息,只是丟失了焦點。
反復的在兩個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實例同時存在。運用實例:瀏覽器主界面。不管跳轉多少頁面,主界面只啟動一次,其余都走onNewIntent,并清空主界面上的其他頁面。
<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定義
Affinity更像是表明了activity屬于哪個task,默認情況下,應用所有的activities都有相同的affinity,所以都是在相同的task中。然后你可以編輯默認的affinity。Activities定義在不同的應用可以共享一個affinity,或者activities定義在相同的應用中可以被不同的affinities所關聯。
你可以編輯在<activity>元素中activity的taskAffinity屬性。
?先看看兩種不同的情況下affinity的表現:
- 當運行一個activity包含了FLAG_ACTIVITY_NEW_TASK標記
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?
另一篇 圖文并茂?http://www.androidchina.net/3173.html?
兩種設置方式:
1) XML
<activity android:name=".ActivitySub"android:launchMode="singleInstance"> </activity>2)在java代碼中設置intent.setFlags() Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(this,ActivitySub.ckass); startActivity(intent);總結
以上是生活随笔為你收集整理的Activity的LaunchMode和taskAffinity的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个人很轴的表现
- 下一篇: 周杰伦最好听的10首歌