Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android launchMode加載模式和intent flag

android launchMode加載模式和intent flag

編輯:關於Android編程

Application,Task和Process的區別與聯系

Task是一個存在於Framework層的概念,容易與它混淆的有Application(應用)和Process(進程)。

Application

application翻譯成中文時一般稱為“應用”或“應用程序”,在android中,總體來說一個應用就是一組組件的集合。眾所周知,android是在應用層組件化程度非常高的系統,android開發的第一課就是學習android的四大組件。當我們寫完了多個組件,並且在manifest文件中注冊了這些組件之後,把這些組件和組件使用到的資源打包成apk,我們就可以說完成了一個application。application和組件的關系可以在manifest文件中清晰地體現出來。

task

task是在程序運行時,只針對activity的概念。說白了,task是一組相互關聯的activity的集合,它是存在於framework層的一個概念,控制界面的跳轉和返回。這個task存在於一個稱為back stack的數據結構中,也就是說,framework是以棧的形式管理用戶開啟的activity。這個棧的基本行為是,當用戶在多個activity之間跳轉時,執行壓棧操作,當用戶按返回鍵時,執行出棧操作。

task是可以跨應用的,這正是task存在的一個重要原因。有的Activity,雖然不在同一個app中,但為了保持用戶操作的連貫性,把他們放在同一個任務中。例如,在我們的應用中的一個Activity A中點擊發送郵件,會啟動郵件程序的一個Activity B來發送郵件,這兩個activity是存在於不同app中的,但是被系統放在一個任務中,這樣當發送完郵件後,用戶按back鍵返回,可以返回到原來的Activity A中,這樣就確保了用戶體驗。

process

process一般翻譯成進程,進程是操作系統內核中的一個概念,表示直接受內核調度的執行單位。在應用程序的角度看,我們用java編寫的應用程序,運行在dalvik虛擬機中,可以認為一個運行中的dalvik虛擬機實例占有一個進程,所以,在默認情況下,一個應用程序的所有組件運行在同一個進程中。但是這種情況也有例外,即,應用程序中的不同組件可以運行在不同的進程中。只需要在manifest中用process屬性指定組件所運行的進程的名字

  
  

四種加載模式

Android Activity 四種啟動模式 lunchMode 在Manifest 配置文件中的屬性如下:
(1) android:launchMode=”standard”(默認)
(2)android:launchMode=”singleTop”
(3)android:launchMode=”singleTask”
(4)android:launchMode=”singleInstance”

standard模式

Activity的默認加載方法,即使某個Activity在 Task棧中已經存在,另一個activity通過Intent跳轉到該activity,同樣會新創建一個實例壓入棧中。例如:現在棧的情況為:A B C D,在D這個Activity中通過Intent跳轉到D,那麼現在的棧情況為: A B C D D 。此時如果棧頂的D通過Intent跳轉到B,則棧情況為:A B C D D B。此時如果依次按返回鍵,D D C B A將會依次彈出棧而顯示在界面上。

singleTop模式

(1) 如果某個Activity的Launch mode設置成singleTop,那麼當該Activity位於棧頂的時候,再通過Intent跳轉到本身這個Activity,則將不會創建一個新的實例壓入棧中。例如:現在棧的情況為:A B C D。D的Launch mode設置成了singleTop,那麼在D中啟動Intent跳轉到D,那麼將不會新創建一個D的實例壓入棧中,此時棧的情況依然為:A B C D。但是如果此時B的模式也是singleTop,D跳轉到B,那麼則會新建一個B的實例壓入棧中,因為此時B不是位於棧頂,此時棧的情況就變成了:A B C D B。

(2)一個singleTop Activity 的實例可以無限多,唯一的區別是如果在棧頂已經有一個相同類型的Activity實例,Intent不會再創建一個Activity,而是通過onNewIntent()被發送到現有的Activity。
在singleTop模式下我們需要同時在onCreate() 和 onNewIntent()中處理發來的intent,以滿足不同情況。

(3)使用場合:這種啟動模式的用例之一就是搜索功能。假設我們創建了一個搜索框,點擊搜索的時候將導航到一個顯示搜索結果列表的SearchActivity中,為了更好的用戶體驗,這個搜索框一般也會被放到SearchActivity中,這樣用戶想要再次搜索就不需要按返回鍵。

(4)而當Intent來自於另外一個應用的時候,新的Activity的啟動方式和standard模式是一致的(Lollipop以及之後:會創建一個新的任務)。

singleTask模式

如果某個Activity是singleTask模式,那麼Task棧中將會只有一個該Activity的實例。例如:現在棧的情況為:A B C D。B的Launch mode為singleTask,此時D通過Intent跳轉到B,則棧的情況變成了:A B。而C和D被彈出銷毀了,也就是說位於B之上的實例都被銷毀了。

singleTask模式的Activity只允許在系統中有一個實例。如果系統中已經有了一個實例,持有這個實例的任務將移動到頂部,同時intent將被通過onNewIntent()發送。如果沒有,則會創建一個新的Activity並置放在合適的任務中。

一旦intent是從另外的應用發送過來,並且系統中也沒有任何Activity的實例,則會創建一個新的任務,並且新的Activity被作為根Activity創建。假設已經有了一個Activity的實例,不管它是在哪個任務中(包括上面的那種情況,在用於這個Activity的應用中),整個任務將被移到頂端,而singleTask Activity上面的所有 Activity 都將被銷毀, 用戶需要按back鍵遍歷玩棧中的Activity才能回到調用者任務。

應用場景:郵件客戶端的收件箱或者社交網絡的時間軸。這些Activity一般不會設計成擁有多個實例,singleTask可以滿足。但是在使用這種模式的時候必須要明智,因為有些Activity會在用戶不知情的情況下被銷毀。

**1. 設置了”singleTask”啟動模式的Activity,它在啟動的時候,會先在系統中查找屬性值affinity等於它的屬性值taskAffinity的任務存在;如果存在這樣的任務,它就會在這個任務中啟動,否則就會在新任務中啟動。因此,如果我們想要設置了”singleTask”啟動模式的Activity在新的任務中啟動,就要為它設置一個獨立的taskAffinity屬性值。
2. 如果設置了”singleTask”啟動模式的Activity不是在新的任務中啟動時,它會在已有的任務中查看是否已經存在相應的Activity實例,如果存在,就會把位於這個Activity實例上面的Activity全部結束掉,即最終這個Activity實例會位於任務的堆棧頂端中。**

singleInstance模式

將Activity壓入一個新建的任務棧中。例如:Task棧1的情況為:A B C。C通過Intent跳轉到D,而D的Launch mode為singleInstance,則將會新建一個Task棧2。此時Task棧1的情況還是為:A B C。Task棧2的情況為:D。此時屏幕界面顯示D的內容,如果這時D又通過Intent跳轉到D,則Task棧2中也不會新建一個D的實例,所以兩個棧的情況也不會變化。而如果D跳轉到C,則棧1的情況變成了:A B C C,因為C的Launch mode為standard,此時如果再按返回鍵,則棧1變成:A B C。也就是說現在界面還顯示C的內容,不是D。

這個模式非常接近於singleTask,系統中只允許一個Activity的實例存在。區別在於持有這個Activity的任務中只能有一個Activity:即這個單例本身。
不過結果卻很怪異,從dumpsys提供的信息來看,似乎系統中有兩個任務但任務管理器中只顯示一個,即最後被移到頂部的那個。導致雖然後台有一個任務在運行,我們卻無法切換回去,這一點也不科學。
因為這個任務只有一個Activity,我們再也無法切回到任務#1了。唯一的辦法是重新在launcher中啟動這個應用。
不過這個問題也有解決方案,就像我們在singleTask Acvity中做的,只要為singleInstance Activity設置taskAffinity屬性就可以了。

使用場景:撥打電話界面

這種模式很少被使用。實際使用的案例如Launcher的Activity或者100%確定只有一個Activity的應用。總之除非完全有必要,不然我不建議使用這種模式。

SingleTask和SingleInstance區別

個人想法:在不指定android:taskAffinity的情況下,SingleTask存在的task是和應用一致的,而SingleInstance是單獨存在一個新的task中,並且不能夠通過返回到底這個界面,只能重新加載。如果其他應用B調用了應用A中的singleTask界面b,如果開始之前,a->b->c,B調用b之後,A棧中變成a->b,c被銷毀,此時點擊後退鍵,先回到a,知道A應用退出後回到應用B的調用頁面。

實例

有這樣的需求,多個Task共享一個Activity(singleTask是在一個task中共享一個Activity)。
現成的例子是google地圖。比如我有一個應用是導游方面的,其中調用的google地圖Activity。那麼現在我比如按home鍵,然後到應用列表中打開google地圖,你會發現顯示的就是剛才的地圖,實際上是同一個Activity。

如果使用前三種模式,是無法實現這個需求的。google地圖應用中有多個上下文Activity,比如路線查詢等的,導游應用也有一些上下文Activity。在各自應用中回退要回退到各自的上下文Activity中。

singleInstance模式解決了這個問題(繞了這麼半天才說到正題)。讓這個模式下的Activity單獨在一個task棧中。這個棧只有一個Activity。導游應用和google地圖應用發送的intent都由這個Activity接收和展示。

這裡又有兩個問題:
如果是這種情況,多個task棧也可以看作一個應用。比如導游應用啟動地圖Activity,實際上是在導游應用task棧之上singleInstance模式創建的(如果還沒有的話,如果有就是直接顯示它)一個新棧,當這個棧裡面的唯一Activity,地圖Activity回退的時候,只是把這個棧移開了,這樣就看到導游應用剛才的Activity了;
多個應用(Task)共享一個Activity要求這些應用都沒有退出,比如剛才強調要用home鍵從導游應用切換到地圖應用。因為,如果退出導游應用,而這時也地圖應用並未運行的話,那個單獨的地圖Activity(task)也會退出了。

startActivityForResult如果被啟動activity加載模式為SingleTask和SingleInstance

被啟動activity加載模式singleTask,獲取不到返回值,0既是RESULT_CANCELED,也就是說直接取消了,onActivityResult收到RESULT_CANCELED
singleInstance,他和singleTask類似,不同點是singleInstance要求activity的實例不僅只有一個,並且整個task中只有一個activity實例,而singleTask所在的棧中允許存在其他activity的實例。

由此我們可以知道設置了singleInstance的activity也具有上面的特性,此外,啟動的activity設置了singleInstance,那麼無論被啟動的activity有沒有設置singleInstance,都無法獲取返回值

使用Intent標志

啟動 Activity 時,您可以通過在傳遞給 startActivity() 的 Intent 中加入相應的標志,修改 Activity 與其任務的默認關聯方式。可用於修改默認行為的標志包括:

FLAG_ACTIVITY_NEW_TASK

在新任務中啟動 Activity。如果已為正在啟動的 Activity 運行任務,則該任務會轉到前台並恢復其最後狀態,同時 Activity 會在 onNewIntent() 中收到新 Intent。
正如前文所述,這會產生與 “singleTask” launchMode 值相同的行為。

FLAG_ACTIVITY_SINGLE_TOP

如果正在啟動的 Activity 是當前 Activity(位於返回棧的頂部),則 現有實例會接收對 onNewIntent() 的調用,而不是創建 Activity 的新實例。
正如前文所述,這會產生與 “singleTop” launchMode 值相同的行為。

FLAG_ACTIVITY_CLEAR_TOP

如果正在啟動的 Activity 已在當前任務中運行,則會銷毀當前任務頂部的所有 Activity,並通過 onNewIntent() 將此 Intent 傳遞給 Activity 已恢復的實例(現在位於頂部),而不是啟動該 Activity 的新實例。
產生這種行為的 launchMode 屬性沒有值。

FLAG_ACTIVITY_CLEAR_TOP 通常與 FLAG_ACTIVITY_NEW_TASK 結合使用。一起使用時,通過這些標志,可以找到其他任務中的現有 Activity,並將其放入可從中響應 Intent 的位置。

SingleTask和FLAG_ACTIVITY_NEW_TASK

當設置了
android:launchMode=”singleTask”
時,系統會查找 對應的taskAffinity任務空間是否有啟動過這個activity ,如果沒有啟動,則在這個taskAffinity 任務空間啟動,如果有啟動過,則將這個activity 推送到棧頂, 那麼在這個activity 之上的activity 都被推送出棧了,不在任務Task 中,也就是 Ondestroy 了。

常用匯總

Intent的常用Flag參數:

FLAG_ACTIVITY_CLEAR_TOP:

例如現在的棧情況為:A B C D 。D此時通過intent跳轉到B,如果這個intent添加FLAG_ACTIVITY_CLEAR_TOP 標記,則棧情況變為:A B。如果沒有添加這個標記,則棧情況將會變成:A B C D B。也就是說,如果添加了FLAG_ACTIVITY_CLEAR_TOP 標記,並且目標Activity在棧中已經存在,則將會把位於該目標activity之上的activity從棧中彈出銷毀。這跟上面把B的Launch mode設置成singleTask類似。

FLAG_ACTIVITY_NEW_TASK:

例如現在棧1的情況是:A B C。C通過intent跳轉到D,並且這個intent添加了FLAG_ACTIVITY_NEW_TASK 標記,如果D這個Activity在Manifest.xml中的聲明中添加了Task affinity,並且和棧1的affinity不同,系統首先會查找有沒有和D的Task affinity相同的task棧存在,如果有存在,將D壓入那個棧,如果不存在則會新建一個D的affinity的棧將其壓入。如果D的Task affinity默認沒有設置,或者和棧1的affinity相同,則會把其壓入棧1,變成:A B C D,這樣就和不加FLAG_ACTIVITY_NEW_TASK 標記效果是一樣的了。 注意如果試圖從非activity的非正常途徑啟動一個activity,比如從一個service中啟動一個activity,則intent比如要添加FLAG_ACTIVITY_NEW_TASK 標記

FLAG_ACTIVITY_NO_HISTORY:

例如現在棧情況為:A B C。C通過intent跳轉到D,這個intent添加FLAG_ACTIVITY_NO_HISTORY標志,則此時界面顯示D的內容,但是它並不會壓入棧中。如果按返回鍵,返回到C,棧的情況還是:A B C。如果此時D中又跳轉到E,棧的情況變為:A B C E,此時按返回鍵會回到C,因為D根本就沒有被壓入棧中。

FLAG_ACTIVITY_SINGLE_TOP:

和上面Activity的 Launch mode的singleTop類似。如果某個intent添加了這個標志,並且這個intent的目標activity就是棧頂的activity,那麼將不會新建一個實例壓入棧中。

Activity的主要屬性:

allowTaskReparenting:設置成true時,和Intent的FLAG_ACTIVITY_NEW_TASK 標記類似。
alwaysRetainTaskStat: 如果用戶長時間將某個task 移入後台,則系統會將該task的棧內容彈出只剩下棧底的activity,此時用戶再返回,則只能看到根activity了。如果棧底的 activity的這個屬性設置成true,則將阻止這一行為,從而保留所有的棧內容。
clearTaskOnLaunch:根activity的這個屬性設置成true時,和上面的alwaysRetainTaskStat 的屬性為true情況搞好相反。
finishOnTaskLaunch:對於任何activity,如果它的這個屬性設置成true,則當task被放置到後台,然後重新啟動後,該activity將不存在了。

處理關聯taskAffinity

“關聯”指示 Activity 優先屬於哪個任務。默認情況下,同一應用中的所有 Activity 彼此關聯。 因此,默認情況下,同一應用中的所有 Activity 優先位於相同任務中。 不過,您可以修改 Activity 的默認關聯。 在不同應用中定義的 Activity 可以共享關聯,或者可為在同一應用中定義的 Activity 分配不同的任務關聯。
可以使用 元素的 taskAffinity 屬性修改任何給定 Activity 的關聯。

taskAffinity 屬性取字符串值,該值必須不同於 在 元素中聲明的默認軟件包名稱,因為系統使用該名稱標識應用的默認任務關聯

作用

啟動 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 標志。

默認情況下,新 Activity 會啟動到調用 startActivity() 的 Activity 任務中。它將推入與調用方相同的返回棧。 但是,如果傳遞給 startActivity() 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 標志,則系統會尋找其他任務來儲存新 Activity。這通常是新任務,但未做強制要求。 如果現有任務與新 Activity 具有相同關聯,則會將 Activity 啟動到該任務中。 否則,將開始新任務。
如果此標志導致 Activity 開始新任務,且用戶按“主頁”按鈕離開,則必須為用戶提供導航回任務的方式。 有些實體(如通知管理器)始終在外部任務中啟動 Activity,而從不作為其自身的一部分啟動 Activity,因此它們始終將 FLAG_ACTIVITY_NEW_TASK 放入傳遞給 startActivity() 的 Intent 中。請注意,如果 Activity 能夠由可以使用此標志的外部實體調用,則用戶可以通過獨立方式返回到啟動的任務,例如,使用啟動器圖標(任務的根 Activity 具有 CATEGORY_LAUNCHER Intent 過濾器;請參閱下面的啟動任務部分)。

Activity 將其 allowTaskReparenting 屬性設置為 “true”。

在這種情況下,Activity 可以從其啟動的任務移動到與其具有關聯的任務(如果該任務出現在前台)。
例如,假設將報告所選城市天氣狀況的 Activity 定義為旅行應用的一部分。 它與同一應用中的其他 Activity 具有相同的關聯(默認應用關聯),並允許利用此屬性重定父級。當您的一個 Activity 啟動天氣預報 Activity 時,它最初所屬的任務與您的 Activity 相同。 但是,當旅行應用的任務出現在前台時,系統會將天氣預報 Activity 重新分配給該任務並顯示在其中。

我遇到的問題記錄

android 按home鍵返回到桌面後,再按桌面應用圖標又重新打開該應用的解決方法。
解決方法:這個問題是因為在AndroidManifest.xml中,你的啟動activity中加了android:launchMode=”singleTask”,將這行代碼去掉就可以了。
 

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