Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Activity跳轉Flag你想知道的都在這了

Activity跳轉Flag你想知道的都在這了

編輯:關於Android編程

Android 的一個特色就是 application A 的 activity 可以啟動 application B 的 activity,盡管 A 和 B 是毫無干系的,而在用戶看來,兩個場景緊密聯系,視覺上二者構成了一個整體。Android 就是把這種誤覺定義為 Task,它既不是 class,也不是 AndroidMainifest.xml 中的一個元素。從表現上看 Task 就像是一個 stack,一個一個的 activity 是構成 stack 的元素,做著入棧 (push) 和出棧 (pop-up)這樣簡單重復性的勞動。

默認的規則總是滿足大多數的應用場景,但是也總會有一些例外打破習以為常的慣例。Task 的默認規則同樣並非牢不可破,修改的方法還是有的。借助 Intent 中的 flag 和 AndroidMainifest.xml 中 activity 元素的屬性,就可以控制到 Task 裡 Activity 的關聯關系和行為。

在 android.content.Intent 中一共定義了20種不同的 flag,其中和 Task 緊密關聯的有四種:

1.FLAG_ACTIVITY_NEW_TASK 2.FLAG_ACTIVITY_CLEAR_TOP 3.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 4.FLAG_ACTIVITY_SINGLE_TOP

在使用這四個 flag 時,一個 Intent 可以設置一個 flag,也可以選擇若干個進行組合。

默認情況下,通過 startActivity() 啟動一個新的 Activity,新的 Activity 將會和調用者在同一個 stack 中。但是,如果在傳遞給 startActivity() 的 Intent 對象裡包含了 FLAG_ACTION_NEW_TASK,情況將發生變化,–系統將為新的 Activity “尋找”一個不同於調用者的 Task。不過要找的 Task 是不是一定就是 NEW 呢?如果是第一次執行,則這個設想成立,如果說不是,也就是說已經有一個包含此 Activity 的Task 存在,則不會再啟動 Activity。

如果 flag 是 FLAG_ACTIVITY_CLEAR_TOP,同時當前的 Task 裡已經有了這個 Activity,那麼情形又將不一樣。Android 不但不會啟動新的 Activity 實例,而且還會將 Task 裡 該 Activity 之上的所有 Activity 一律結束掉,然後將 Intent 發給這個已存在的 Activity。Activity 收到 Intent 之後,可以在 onNewIntent() 裡做下一步的處理,也可以自行結束然後重新創建自己。如果 Activity 在 AndroidMainifest.xml 裡將啟動模式設置成”multiple”,– 默認模式,並且 Intent 裡也沒有設置 FLAG_ACTIVITY_SINGLE_TOP,那麼它將選擇後者。否則,它將選擇前者。FLAG_ACTIVITY_CLEAR_TOP 還可以和 FLAG_ACTION_NEW_TASK 配合使用。

如果 flag 設置的是 FLAG_ACTIVITY_SINGLE_TOP,則意味著如果 Activity 已經是運行在 Task 的 top,則該 Activity 將不會再被啟動。

設置intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

下面是關於setFlags的一下屬性的解釋:
一、Activity和Task(棧)的關系
Task就像一個容器,而Activity就相當與填充這個容器的東西,第一個東西(Activity)則會處於最下面,最後添加的東西(Activity)則會在最低端。從Task中取出東西(Activity)則是從最頂端取出,也就是說最先取出的是最後添加的東西(Activity),一次類推,最後取出的是第一次添加的Activity,而Activity在Task中的順序是可以控制的,那則在Activity跳轉時用到Intent Flag
二、界面跳轉和服務的啟動都會用到Intent,現在介紹Intent Flag是關於Activity的跳轉
Intent intent = new Intent(this,xxx.class);
//如果activity在task存在,拿到最頂端,不會啟動新的Activity
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//如果activity在task存在,將Activity之上的所有Activity結束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默認的跳轉類型,將Activity放到一個新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//如果Activity已經運行到了Task,再次跳轉不會在運行這個Activity
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
來自:http://www.anddevs.com/?p=118

1.Activity的affinity(親和力)
2.Intent幾種常見的flags
3.與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(參考的taskAffinity屬性)繼承而來,而Application默認的affinity是中的包名,我們可以為設置taskAffinity屬性值,這樣可以應用到下的所有,也可以單獨為某個Activity設置taskAffinity。例如:在系統自帶的Browser中,package為com.android.browser,但是卻自定義一個taskAffinity屬性值:

  

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配置如下:

  

              

                  

                  

              

          

然後,在appB中的FirstActivity跳轉代碼如下:

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

startActivity(intent);  

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

這裡寫圖片描述

這裡寫圖片描述

再次啟動appB應用:

這裡寫圖片描述

啟動appA應用:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160622/20160622091046737.gif" title="\" />

我們發現在從appB跳轉到appA的SecondActivity之後,SecondActivity實例好像是嵌入到了appB中,但是不影響appA的正常運行,這種關系如下圖所示:

這裡寫圖片描述

然後我們修改一下跳轉的代碼:

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

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  

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

Intent intent = new Intent(this, SecondActivity.class);  

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  

startActivity(intent);  

然後跳轉後SecondActivity實例如下:

這裡寫圖片描述

從序列號可以看到這兩個實例是不同的,證明它是經過了銷毀和重新的過程。
然後我們把ThirdActivity中的跳轉代碼添加FLAG_ACTIVITY_SINGLE_TOP標記:

Intent intent = new Intent(this, SecondActivity.class);  

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);  

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可以加深理解。
的task相關屬性
在中定義了幾個常見的task相關屬性,它們分別代表了task內部不同的行為特征,我們就來逐個介紹一下:
1.android:allowTaskReparenting
這個屬性用來標記一個Activity實例在當前應用退居後台後,是否能從啟動它的那個task移動到有共同affinity的task,“true”表示可以移動,“false”表示它必須呆在當前應用的task中,默認值為false。如果一個這個Activity的元素沒有設定此屬性,設定在上的此屬性會對此Activity起作用。例如在一個應用中要查看一個web頁面,在啟動系統浏覽器Activity後,這個Activity實例和當前應用處於同一個task,當我們的應用退居後台之後用戶再次從主選單中啟動應用,此時這個Activity實例將會重新宿主到Browser應用的task內,在我們的應用中將不會再看到這個Activity實例,而如果此時啟動Browser應用,就會發現,第一個界面就是我們剛才打開的web頁面,證明了這個Activity實例確實是宿主到了Browser應用的task內。我們就來結合實例演示一下這個過程:
首先,在appB的FirstActivity中,我們將跳轉動作做以下改動:

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

startActivity(viewIntent); 

進入appB時的界面:
這裡寫圖片描述

啟動web界面之後:

這裡寫圖片描述

然後我們按Home鍵,是當前應用退居後台,我們回到主選單,重新啟動appB,界面如下:

這裡寫圖片描述

此時我們在主選單中啟動Browser應用,出現在我們眼前的界面是這樣的:

這裡寫圖片描述

以上這種行為也證明了我們前面的論斷,為了更清楚的說明問題,也為了讓大家自己可以驗證,下面我們要再次演示一下appB和appA的啟動過程:
對於appA,在上面的基礎上,不用修改其他地方,只需為SecondActivity的元素添加一個屬性,如下:

  

...           

  

然後,在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相同,我們想一下也許就明白了,原來它是appA╧y"http://zz.2cto.com/seo/" target="_blank" class="keylink">seo4eO5/cC0tcSjrNTZxvS2r2FwcEHKsbKizrTJ+rPJyM66ztDCtcRBY3Rpdml0ecq1wP2ho9XiuPbKsbryyOe5+87Sw8ewtM/CuvPNy7z8o6xhcHBBvs274cGivLTNy7P2o6zWpMP3wcu0y8qxYXBwQbXEdGFza8Dv1rvT0NK7uPZBY3Rpdml0ecq1wP2jrNKyvs3Kx9XiuPZTZWNvbmRBY3Rpdml0ecq1wP2hozxiciAvPg0K0OjSqtei0uK1xMrHo6zI57n7YXBwQs3LvtO688yo1q6686Osw7vT0NTZtM7G9LavYXBwQqOstvjKx9axvdPG9LavYXBwQaOsvauyu7vhs/bP1tLUyc/P1s/zoaPW2NDCy97W97XEtq/X97eiyfrU2mFwcELU2bTOxvS2r7XEuf2zzNbQoaM8YnIgLz4NCmFuZHJvaWQ6YWxsb3dSZXBhcmVudGluZ7XE0Ke5+828yOfPwqO6PC9wPg0KPHA+PGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20160622/20160622091049761.gif" title="\" />

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

  

        ...      

  

FristActivity界面如下:

這裡寫圖片描述

然後我們讓FirstActivity跳轉到SecondActivity,結果如下:

這裡寫圖片描述

然後我們按Home鍵回到主界面,再次啟動appB,我們看到以下結果:

這裡寫圖片描述

我們看到,再次啟動appB時,我們只能看到FirstActivity界面,此時在FirstActivity之上的所有Activity都已經被清除出棧。示意圖如下:

這裡寫圖片描述

4.android:finishOnTaskLaunch
這個屬性和android:allowReparenting屬性相似,不同之處在於allowReparenting屬性是重新宿主到有共同affinity的task中,而finishOnTaskLaunch屬性是銷毀實例。如果這個屬性和android:allowReparenting都設定為“true”,則這個屬性勝出。
 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved