【轉】Android總結篇系列:Activity Intent Flags及Task相關屬性

【轉】Android總結篇系列:Activity Intent Flags及Task相關屬性html

同上文同樣,本文主要引用自網上現有博文,並加上一些本身的理解,在此感謝原做者。android

原文地址:web

http://blog.csdn.net/liuhe688/article/details/6761337瀏覽器

--------------------------------------------------------------------------app

今天咱們來說一下Activity的task相關內容。函數

上次咱們講到Activity的四種啓動模式的時候,已經瞭解到一些關於task的技術,今天我再向你們介紹一下。task是一個具備棧結構的容器,能夠放置多個Activity實例。啓動一個應用,系統就會爲之建立一個task,來放置根Activity;默認狀況下,一個Activity啓動另外一個Activity時,兩個Activity是放置在同一個task中的,後者被壓入前者所在的task棧,當用戶按下後退鍵,後者從task被彈出,前者又顯示在幕前,特別是啓動其餘應用中的Activity時,兩個Activity對用戶來講就好像是屬於同一個應用;系統task和task之間是互相獨立的,當咱們運行一個應用時,按下Home鍵回到主屏,啓動另外一個應用,這個過程當中,以前的task被轉移到後臺,新的task被轉移到前臺,其根Activity也會顯示到幕前,過了一會以後,在此按下Home鍵回到主屏,再選擇以前的應用,以前的task會被轉移到前臺,系統仍然保留着task內的全部Activity實例,而那個新的task會被轉移到後臺,若是這時用戶再作後退等動做,就是針對該task內部進行操做了。測試

咱們今天就講一下和task相關的知識,主要分一下幾點:this

1.Activity的affinity(親和力)google

2.Intent幾種常見的flagsspa

3.<activity>與task相關屬性

affinity:

擁有相同affinity的多個Activity理論同屬於一個task,task自身的affinity決定於根Activity的affinity值。affinity在什麼場合應用呢?

1.根據affinity從新爲Activity選擇宿主task(與allowTaskReparenting屬性配合工做);

2.啓動一個Activity過程當中Intent使用了FLAG_ACTIVITY_NEW_TASK標記,根據affinity查找或建立一個新的具備對應affinity的task。咱們會在後面進行詳細講解。

默認狀況下,一個應用內的全部Activity都具備相同的affinity,都是從Application(參考<application>的taskAffinity屬性)繼承而來,而Application默認的affinity是<manifest>中的包名,咱們能夠爲<application>設置taskAffinity屬性值,這樣能夠應用到<application>下的全部<activity>,也能夠單獨爲某個Activity設置taskAffinity。例如:在系統自帶的Browser中,package爲com.android.browser,可是<application>卻自定義一個taskAffinity屬性值:

1 <application android:name="Browser"
2  android:label="@string/application_name"
3  android:icon="@drawable/ic_launcher_browser"
4  android:backupAgent=".BrowserBackupAgent"
5  android:taskAffinity="android.task.browser" >

Intent幾種常見flags:

在android.content.Intent中定義了若干個flags,其中最重要的有如下幾個:

1.FLAG_ACTIVITY_NEW_TASK

當Intent對象包含這個標記時,系統會尋找或建立一個新的task來放置目標Activity,尋找時依據目標Activity的taskAffinity屬性進行匹配,若是找到一個task的taskAffinity與之相同,就將目標Activity壓入此task中,若是查找無果,則建立一個新的task,並將該task的taskAffinity設置爲目標Activity的taskActivity,將目標Activity放置於此task。注意,若是同一個應用中Activity的taskAffinity都使用默認值或都設置相同值時,應用內的Activity之間的跳轉使用這個標記是沒有意義的,由於當前應用task就是目標Activity最好的宿主。下面咱們會經過實例進行演示這個特性:

咱們新建兩個項目,分別命名爲appA和appB,而且分別建立FirstActivity和SecondActivity,咱們準備讓appB中的FirstActivity跳轉到appA的SecondActivity。appA中的SecondActivity配置以下:

1 <activity android:name=".SecondActivity">
2     <intent-filter>
3         <action android:name="android.intent.action.APP_A_SECOND_ACTIVITY" />
4         <category android:name="android.intent.category.DEFAULT" />
5     </intent-filter>
6 </activity>

而後,在appB中的FirstActivity跳轉代碼以下:

1 Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY"); 2 startActivity(intent);

咱們要演示幾個步驟:1.在appB中的FirstActivity點擊按鈕跳轉到appA中的SecondActivity;2.按Home鍵回到主屏,在主選單中再次啓動appB;3.按Home鍵回到主屏,在主選單中啓動appA。演示過程如圖所示:

再次啓動appB應用:

啓動appA應用:

咱們發如今從appB跳轉到appA的SecondActivity以後,SecondActivity實例好像是嵌入到了appB中,可是不影響appA的正常運行,這種關係以下圖所示:

而後咱們修改一下跳轉的代碼:

1 Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY"); 2 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3 startActivity(intent);

咱們加上了FLAG_NEW_TASK標記,在來看一下演示結果:

再次啓動appB:

啓動appA:

咱們看到差異了吧,當咱們再次啓動appB時已經看不到剛纔啓動的appA中的SecondActivity,而啓動appA時卻直接看到了,說明這個SecondActivity實例並不在appB的task內,而是建立了一個task,這個task的affinity就是SecondActivity默認的affinity,因爲appA的SecondActivity的affinity是從Application繼承而來,因此當appA啓動時會直接找到這個task,而不是建立新的task。咱們看一下解析圖:

因而可知, FLAG_ACTIVITY_NEW_TASK應該這樣去理解:根據Activity Affinity判斷是否須要建立新的Task,而後再建立新的Activit實例放進去。

 

2.FLAG_ACTIVITY_CLEAR_TOP:

當Intent對象包含這個標記時,若是在棧中發現存在Activity實例,則清空這個實例之上的Activity,使其處於棧頂。例如:咱們的FirstActivity跳轉到SecondActivity,SecondActivity跳轉到ThirdActivity,而ThirdActivity又跳到SecondActivity,那麼ThirdActivity實例將被彈出棧,使SecondActivity處於棧頂,顯示到幕前,棧內只剩下FirstActivity和SecondActivity。這個SecondActivity既能夠在onNewIntent()中接收到傳來的Intent,也能夠把本身銷燬以後從新啓動來接受這個Intent。在使用默認的「standard」啓動模式下,若是沒有在Intent使用到FLAG_ACTIVITY_SINGLE_TOP標記,那麼它將關閉後重建,若是使用了這個FLAG_ACTIVITY_SINGLE_TOP標記,則會使用已存在的實例;對於其餘啓動模式,無需再使用FLAG_ACTIVITY_SINGLE_TOP,它都將使用已存在的實例,Intent會被傳遞到這個實例的onNewIntent()中。

下面咱們來驗證一下這個過程:

首先,Activity啓動模式都按照默認值「standard」。從FirstActivity跳轉到SecondActivity,SecondActivity實例以下:

從ThirdActivity跳轉到SecondActivity時,跳轉代碼以下:

1 Intent intent = new Intent(this, SecondActivity.class); 2 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 3 startActivity(intent);

而後跳轉後SecondActivity實例以下:

從序列號能夠看到這兩個實例是不一樣的,證實它是通過了銷燬和從新的過程。

而後咱們把ThirdActivity中的跳轉代碼添加FLAG_ACTIVITY_SINGLE_TOP標記:

1 Intent intent = new Intent(this, SecondActivity.class); 2 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 3 startActivity(intent);

兩次實例均以下圖所示:

若是咱們不想添加FLAG_ACTIVITY_SINGLE_TOP,那麼把SecondActivity的啓動模式改成「standard」以外的三種便可,效果和上面同樣,都不會建立新的實例。

簡單點理解:FLAG_ACTIVITY_CLEAR_TOP表示啓動的Activity會將Task中位於其上的Activity都強制出棧,使其自身位於棧頂。在Standard模式下,若是原來的Activity棧順序爲 A -> B1 -> D, 此時D經過FLAG_ACTIVITY_CLEAR_TOP啓動B,則棧順序爲A -> B2。對於同時設置了FLAG_ACTIVITY_SINGLE_TOP,則棧順序爲 A-> B1(此時回調onNewIntent()..),此時效果與Activity啓動模式中的singleTask相同。

 

3.FLAG_ACTIVITY_SINGLE_TOP:

當task中存在目標Activity實例而且位於棧的頂端時,再也不建立一個新的,直接利用這個實例。咱們在上邊的例子中也有講到。

目前發現與Activity啓動模式中的singleTop效果相同。

 

4.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET

若是一個Intent中包含此屬性,則它轉向的那個Activity以及在那個Activity其上的全部Activity都會在task重置時被清除出task(前提:FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)。當咱們將一個後臺的task從新回到前臺時,系統會在特定狀況下爲這個動做附帶一個FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記,意味着必要時重置task,這時FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET就會生效。通過測試發現,對於一個處於後臺的應用,若是在主選單點擊應用,這個動做中含有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記,長按Home鍵,而後點擊最近記錄,這個動做不含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記,因此前者會清除,後者不會。關於這個標記,能夠下圖示之:

 

 

這個標記對於應用存在分割點的狀況會很是有用。好比咱們在應用主界面要選擇一個圖片,而後咱們啓動了圖片瀏覽界面,可是把這個應用從後臺恢復到前臺時,爲了不讓用戶感到困惑,咱們但願用戶仍然看到主界面,而不是圖片瀏覽界面,這個時候咱們就要在轉到圖片瀏覽界面時的Intent中加入此標記。

 

5.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

這個標記在如下狀況下會生效:1.啓動Activity時建立新的task來放置Activity實例;2.已存在的task被放置於前臺。系統會根據affinity對指定的task進行重置操做,task會壓入某些Activity實例或移除某些Activity實例。咱們結合上面的CLEAR_WHEN_TASK_RESET能夠加深理解。

 

<activity>的task相關屬性

 Android中與Task相關的屬性有以下幾種,其中,launchMode和taskAffinity前文已經有所介紹。

taskAffinity
launchMode
allowTaskReparenting
alwaysRetainTaskState
clearTaskOnLaunch
finishOnTaskLaunch

noHistory

---------------------------------------------------------------

在<activity>中定義了幾個常見的task相關屬性,它們分別表明了task內部不一樣的行爲特徵,咱們就來逐個介紹一下:

1.android:allowTaskReparenting

這個屬性用來標記一個Activity實例在當前應用退居後臺後,是否能從啓動它的那個task移動到有共同affinity的task,「true」表示能夠移動,「false」表示它必須呆在當前應用的task中,默認值爲false。若是一個這個Activity的<activity>元素沒有設定此屬性,設定在<application>上的此屬性會對此Activity起做用。例如在一個應用中要查看一個web頁面,在啓動系統瀏覽器Activity後,這個Activity實例和當前應用處於同一個task,當咱們的應用退居後臺以後用戶再次從主選單中啓動應用,此時這個Activity實例將會從新宿主到Browser應用的task內,在咱們的應用中將不會再看到這個Activity實例,而若是此時啓動Browser應用,就會發現,第一個界面就是咱們剛纔打開的web頁面,證實了這個Activity實例確實是宿主到了Browser應用的task內。咱們就來結合實例演示一下這個過程:

首先,在appB的FirstActivity中,咱們將跳轉動做作如下改動:

1 Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.hk")); 2 startActivity(viewIntent);

進入appB時的界面:


啓動web界面以後:

而後咱們按Home鍵,是當前應用退居後臺,咱們回到主選單,從新啓動appB,界面以下:

此時咱們在主選單中啓動Browser應用,出如今咱們眼前的界面是這樣的:

以上這種行爲也證實了咱們前面的論斷,爲了更清楚的說明問題,也爲了讓你們本身能夠驗證,下面咱們要再次演示一下appB和appA的啓動過程:

對於appA,在上面的基礎上,不用修改其餘地方,只需爲SecondActivity的<activity>元素添加一個屬性,以下:

<activity android:name=".SecondActivity" android:allowTaskReparenting="true"> ... </activity>

而後,在appB中的FirstActivity跳轉代碼改成:

Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY"); startActivity(intent);

咱們啓動appB,看到一下界面:

而後點擊按鈕,跳轉到appA中的SecondActivity,界面以下:

此時appB中的FirstActivity和appA中的SecondActivity處於同一個task中taskid爲28,而後咱們按下Home鍵,在主選單中再次啓動appB,咱們發現appA的SecondActivity不見了,咱們看到的是:

而後咱們啓動appA,這是咱們不會看到它的FirstActivity,而是看到了它的SecondActivity:

一般兩個應用分別有本身的task,它們的taskid確定不一樣,但這裏的SecondActivity卻顯示taskid與appB相同,咱們想一下也許就明白了,原來它是appB遷徙過來的,再啓動appA時並未生成任何新的Activity實例。這個時候若是咱們按下後退鍵,appA就會當即退出,證實了此時appA的task裏只有一個Activity實例,也就是這個SecondActivity實例。

須要注意的是,若是appB退居後臺以後,沒有再次啓動appB,而是直接啓動appA,將不會出現以上現象。從新宿主的動做發生在appB再次啓動的過程當中。

android:allowReparenting的效果圖以下:

 

2.android:alwaysRetainTaskState

這個屬性用來標記應用的task是否保持原來的狀態,「true」表示老是保持,「false」表示不可以保證,默認爲「false」。此屬性只對task的根Activity起做用,其餘的Activity都會被忽略。

默認狀況下,若是一個應用在後臺呆的過久例如30分鐘,用戶從主選單再次選擇該應用時,系統就會對該應用的task進行清理,除了根Activity,其餘Activity都會被清除出棧,可是若是在根Activity中設置了此屬性以後,用戶再次啓動應用時,仍然能夠看到上一次操做的界面。

這個屬性對於一些應用很是有用,例如Browser應用程序,有不少狀態,好比打開不少的tab,用戶不想丟失這些狀態,使用這個屬性就極爲恰當。

 

3.android:clearTaskOnLaunch

這個屬性用來標記是否從task清除除根Activity以外的全部的Activity,「true」表示清除,「false」表示不清除,默認爲「false」。一樣,這個屬性也只對根Activity起做用,其餘的Activity都會被忽略。

若是設置了這個屬性爲「true」,每次用戶從新啓動這個應用時,都只會看到根Activity,task中的其餘Activity都會被清除出棧。若是咱們的應用中引用到了其餘應用的Activity,這些Activity設置了allowTaskReparenting屬性爲「true」,則它們會被從新宿主到有共同affinity的task中。

無圖無真相,咱們就來以實例演示一下這個過程,咱們首先修改appB的根Activity的<activity>元素,以下:

1 <activity android:name=".FirstActivity"
2  android:clearTaskOnLaunch="true">
3  ... 4 </activity>

FristActivity界面以下:

而後咱們讓FirstActivity跳轉到SecondActivity,結果以下:

而後咱們按Home鍵回到主界面,再次啓動appB,咱們看到如下結果:

咱們看到,再次啓動appB時,咱們只能看到FirstActivity界面,此時在FirstActivity之上的全部Activity都已經被清除出棧。示意圖以下:

 

 

4.android:finishOnTaskLaunch

這個屬性和android:allowTaskReparenting屬性類似,不一樣之處在於allowTaskReparenting屬性是從新宿主到有共同affinity的task中,而finishOnTaskLaunch屬性是銷燬實例。若是這個屬性和android:allowReparenting都設定爲「true」,則這個屬性勝出。

對於clearTaskOnLaunch與finishOnTaskLaunch之間的區別須要注意下。

 

5.android:noHistory

具備此屬性標識的Activity當導航到其餘Activity上時,Activity棧將不記錄其自身,簡單來講,當A -> B, 此時Activity棧中只含有B。

經測試,其onDeatroy函數回調時機是在B中按Back鍵時觸發。

相關文章
相關標籤/搜索