android的task任務棧

轉自http://blog.csdn.net/liuhe688/article/details/6761337html

古人學問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。南宋.陸遊《冬夜讀書示子聿(yù)》java

軟件行業也是同樣,多少前輩竭盡全力的奮鬥纔出現了軟件行業的繁榮的景象,其中已有很多成爲大師級人物。今天咱們站在偉人的肩膀上,天然會有很多的優點,但不要忘了,要在對技術的認知方面有所提高,仍需咱們去實踐,去實踐。android

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

上次咱們講到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相關的知識,主要分一下幾點:app

1.Activity的affinity(親和力)測試

2.Intent幾種常見的flagsthis

3.<activity>與task相關屬性google

affinity:spa

task對於Activity來講就好像它的身份證同樣,能夠告訴所在的task,本身屬於這個task中的一員;擁有相同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屬性值:

 

[html]  view plain copy
 
  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配置以下:

 

[html]  view plain copy
 
  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跳轉代碼以下:

 

 

[java]  view plain copy
 
  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的正常運行,這種關係以下圖所示:

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

 

[java]  view plain copy
 
  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。咱們看一下解析圖:

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時,跳轉代碼以下:

 

[java]  view plain copy
 
  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標記:

 

[java]  view plain copy
 
  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」以外的三種便可,效果和上面同樣,都不會建立新的實例。

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

4.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:若是一個Intent中包含此屬性,則它轉向的那個Activity以及在那個Activity其上的全部Activity都會在task重置時被清除出task。當咱們將一個後臺的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相關屬性

在<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中,咱們將跳轉動做作如下改動:

 

[java]  view plain copy
 
  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>元素添加一個屬性,以下:

 

[html]  view plain copy
 
  1. <activity android:name=".SecondActivity" android:allowTaskReparenting="true">  
  2. ...           
  3. </activity>  

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

 

 

[java]  view plain copy
 
  1. Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");  
  2. 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>元素,以下:

 

[html]  view plain copy
 
  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:allowReparenting屬性類似,不一樣之處在於allowReparenting屬性是從新宿主到有共同affinity的task中,而finishOnTaskLaunch屬性是銷燬實例。若是這個屬性和android:allowReparenting都設定爲「true」,則這個屬性勝出。

以上就是今天總結的內容,這些都是經常使用的知識,除此以外還有不少等着咱們去探索,繼續努力。

相關文章
相關標籤/搜索