android的task任務棧

來源:互聯網
上載者:User

標籤:

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

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

軟體行業也是一樣,多少前輩不遺餘力的奮鬥才出現了軟體行業的繁榮的景象,其中已有不少成為大師級人物。今天我們站在偉人的肩膀上,自然會有不少的優勢,但不要忘了,要在對技術的認知方面有所提升,仍需我們去實踐,去實踐。

今天我們來講一下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相關的知識,主要分一下幾點:

1.Activity的affinity(親和力)

2.Intent幾種常見的flags

3.<activity>與task相關屬性

affinity:

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屬性值:

 

  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。我們看一下解析圖:

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”之外的三種即可,效果和上面一樣,都不會建立新的執行個體。

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

 

  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>元素添加一個屬性,如下:

 

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

然後,在appB中的FirstActivity跳轉代碼改為:

 

 

  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>元素,如下:

 

  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”,則這個屬性勝出。

以上就是今天總結的內容,這些都是常用的知識,除此之外還有很多等著我們去探索,繼續努力。

android的task任務棧

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.