對用戶來說,這個地圖查看器看起來就像是你的應用程序的一部分,即使它定義在另外的應用程序中並運行在那個程序的進程中。Android 通過保持所有的activity在同一個任務中來保持用戶體驗。簡單的的說,任務就是用戶所體驗到的“應用程序”。它是一組相關的activity,分配到一個棧中。棧中的根activity,是任務的開始——一般來說,它是用戶組應用程序加載器中選擇的activity。在棧頂的activity正是當前正在運行的——集中處理用戶動作的那個。當一個activity啓動了另外一個,這個新的activity將壓入棧中,它將成爲正在運行中的 activity。前一個activity保留在棧中。當用戶按下後退按鍵,當前的這個activity將中棧中彈出,而前面的那個activity恢復成運行中狀態。
棧包含了對象,如果一個棧有多於一個相同的Activity的子類的實例打開——比如,多個地圖查看器——這個棧分別擁有每個實例的入口。棧中的activity不能重新排列,只能壓入和彈出。
任務是一些activity組成的棧,不是清單文件中的類或元素。所以沒有辦法在獨立於它包含的activity的條件下,設置它的值。任務的值作爲一個整體設置在根activity中。例如,下一節將討論“任務的親和性”;這個值是從根activity親和性中讀取出來的。
一個任務中的所有activity一起作爲一個單元。整個任務(整個activity棧)可以移動到前臺或者後臺.假設,例如,當前的任務有四個 activity在棧中——三個在當前的activity之下。用戶按下了HOME鍵,進入了應用程序加載器,選擇了一個新的程序(實際上,是一個新的任務)。當前的任務進入了後臺,新任務的根activity顯示出來。然後,過了一會,用戶退回到主界面,又重新選擇了前一個應用程序(前一個任務),棧中有四個activity的那個任務,現在出現在了前臺。當用戶按下BACK按鍵,屏幕就不會再顯示用戶剛剛離開的那個activity,而是刪除棧頂的 activity,同任務中的前一個activity將被顯示出來。
剛纔說明的那些行爲,是activity和任務的默認行爲。但是有也辦法修改它的所有方面。activity和任務的關聯,activity在任務中的行爲,受控於啓動activity的行爲對象的標誌位和清單文件中的<activity> 元素的屬性的互相作用。請求者和相應着都要說明發生了什麼。
在這裏,主要的行爲標誌爲是:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
主要的<activity> 屬性是:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
下面一節將說明這些標誌和屬性都有什麼用,他們之間怎麼互相影響,應該用什麼樣的方案來控制它們的使用。
親和性和新任務
默認情況下,應用程序中的所有activity,都有一個對於其它activity的親和性—這是一個對於同一個任務中的其他activity的優先權,然後,通過 <activity>元素的 taskAffinity 屬性可以可以分別爲每一個activity設置親和性。不同應用程序定義的activity可以共享同一個親和性,或者同一個應用程序定義的 activity可以指定不同的親和性。親和性在兩種情況下發揮作用:當行爲對象啓動了一個包含 FLAG_ACTIVITY_NEW_TASK標誌的activity,和當一個activity的allowTaskReparenting 屬性設置爲“true”。
FLAG_ACTIVITY_NEW_TASK 標誌
正如前面描述的,一個新的activity,默認情況下,被加載進調用startActivity()方法的activity對象所在的那個任務中。它被壓入和調用者所在的同一個棧中,但是,如果行爲對象在調用startActivity()方法時傳遞了FLAG_ACTIVITY_NEW_TASK標記,系統將用一個不同的任務來容納這個新的activity。通常,就像這個標記的名字所代表的。它是一個新任務,但是,它不必非要這樣。如果已經存在一個和這個activity親和性相同的任務,這個activity就會載入到那個任務中,如果不是的話,纔會啓動新任務。
allowTaskReparenting 屬性
如果activity的allowTaskReparenting 屬性設置爲“true”,它就能從他啓動時所在的任務移動到另一個出現在前臺的任務。例如,假設有一個activity可以根據選擇的城市包括天氣情況,它作爲一個旅行應用程序的一部分。它和同一個應用程序中的其他activity有同樣的親和性(默認的親和性)並且允許重組。你的一個activity開啓了天氣報告器,所以它屬於同一個任務中的這個activity,然而,當旅行應用程序開始運行時,天氣報告器將被重新分配並顯示到那個任務中。
啓動模式
有4中不同的啓動模式可以分配給 <activity> 元素的 launchMode 屬性。
"standard" (默認的模式)
"singleTop "
"singleTask"
"singleInstance"
它們主要有如下不同:
1. 如何決定所屬task
“standard”和”singleTop”的activity的目標task,和收到的Intent的發送者在同一個task內,除非intent包括參數FLAG_ACTIVITY_NEW_TASK。
如果提供了FLAG_ACTIVITY_NEW_TASK參數,會啓動到別的task裏。
“singleTask”和”singleInstance”總是把activity作爲一個task的根元素,他們不會被啓動到一個其他task裏。
應該說”singleInstance”總是把activity作爲一個task的根元素,SingleTask就不一定了。
2. 是否允許多個實例
“standard”和”singleTop”可以被實例化多次,並且存在於不同的task中,且一個task可以包括一個activity的多個實例;
“singleTask”和”singleInstance”則限制只生成一個實例,並且是task的根元素。
singleTop要求如果創建intent的時候棧頂已經有要創建 的Activity的實例,則將intent發送給該實例,而不發送給新的實例。
3. 是否允許其它activity存在於本task內
“singleInstance”獨佔一個task,其它activity不能存在那個task裏;如果它啓動了一個新的activity,不管新的activity的launch mode 如何,新的activity都將會到別的task裏運行(如同加了FLAG_ACTIVITY_NEW_TASK參數)。
而另外三種模式,則可以和其它activity共存。
4. 是否每次都生成新實例
“standard”對於沒一個啓動Intent都會生成一個activity的新實例;
“singleTop”的activity如果在task的棧頂的話,則不生成新的該activity的實例,直接使用棧頂的實例,否則,生成該activity的實例。
比如現在task棧元素爲A-B-C-D(D在棧頂),這時候給D發一個啓動intent,如果D是 “standard”的,則生成D的一個新實例,棧變爲A-B-C-D-D。
如果D是singleTop的話,則不會生產D的新實例,棧狀態仍爲A-B-C-D
如果這時候給B發Intent的話,不管B的launchmode是”standard” 還是 “singleTop” ,都會生成B的新實例,棧狀態變爲A-B-C-D-B。
“singleInstance”是其所在棧的唯一activity,它會每次都被重用。
“singleTask”如果在棧頂,則接受intent,否則,該intent會被丟棄,但是該task仍會回到前臺。
當已經存在的activity實例處理新的intent時候,會調用onNewIntent()方法
如果收到intent生成一個activity實例,那麼用戶可以通過back鍵回到上一個狀態;如果是已經存在的一個activity來處理這個intent的話,用戶不能通過按back鍵返回到這之前的狀態。
總結:
standard 每次都會新建,每個Task都可以有,且每個Task都可以有多個實例(每個Task都可以有,且可以有多個)
singleTop 當前實例如果在棧頂,就不新建實例,調用其OnNewIntent。 如不在棧頂,則新建實例 (每個Task都可以有,且可以有多個,在棧頂時可複用)
singleTask 新建一個Task,如果已經有其他的Task並且包含該實例,那就直接調用那個Task的實例。(只有一個Task中會有)
singleInstance 新建一個Task,且在該Task中只有它的唯一一個實例。 (只有一個Task會有,且該Task中只有它)
FLAG_ACTIVITY_NEW_TASK 類似singleTask
FLAG_ACTIVITY_SINGLE_TOP 類似singleTop
FLAG_ACTIVITY_CLEAR_TOP 無對應
配兩個圖,我覺得很清晰:
(1)standard
(2)singleTask
可以將TaskA TaskB理解爲兩個應用,比如TaskA是你當前的應用,TaskB是默認瀏覽器。
當到了TaskB中後,按返回鍵,需要先在TaskB中返回到root,然後才能返回到TaskA。
清理棧
如果用戶離開一個任務很長時間。系統將清除除了根activity之外的所有activity。當用戶重新回到任務中時,像是用戶離開了它,除了只有最初的activity還在。這個理念是這樣的,過了一段時間,用戶很可能放棄之前所做的事情,回到任務去做一些新的事情。
這只是默認情況,有一些activity的屬性可以控制和修改它。
alwaysRetainTaskState 屬性
如果一個任務的根activity的這個屬性設置成了"true",那麼剛纔提到的那些默認行爲就不會發生。這個任務保留所有的activity,甚至經過了很長一段時間。
clearTaskOnLaunch 屬性
如果任務的根activity的這個屬性設置成了”true“,那麼只要用戶離開了任務並返回,就會清除除了根activity之外的所有activity。換句話說,它和alwaysRetainTaskState正好相反,當用戶返回到任務時,總是恢復到最初的狀態,不管離開了多長時間。
finishOnTaskLaunch 屬性
這個屬性和clearTaskOnLaunch類似,但是它作用於單個activity,而不是整個任務。它可以導致任何的activity離開,包括根activity。當它設置成"true"的時候,作爲任務一部分的activity只對當前會話有效。如果用戶離開然後返回到任務中。它將不再出現。
還有其他的方法強制將activity從棧中移除。如果一個行爲對象包含了
FLAG_ACTIVITY_CLEAR_TOP 標誌,它的目標任務中已經有了一個這樣類型的activity實例,所有棧中位於這個實例之上的activity都會被清除,所以這個實例就會出現在棧頂並且對行爲進行響應。如果activity被設計成"standard"模式,它也將會從棧中被清除,並且會啓動新的實例來處理到來的行爲。這是因爲當設置成”standard“模式後,對每個新的行爲都會創建一個新的實例。
FLAG_ACTIVITY_CLEAR_TOP經常
和FLAG_ACTIVITY_NEW_TASK一起使用。當一起使用時,這些標誌是定位一個在另一個任務中存在的activity並且將它放在一個可以響應行爲的地方的一種方法。
啓動任務
Activity通過將行爲過濾器”android
.intent.action.MAIN“設置爲指定動作和"android .intent.category.LAUNCHER"作爲指定類型,來成爲任務的入口。(前面關於行爲過濾器那一些討論的例子)。這種類型的過濾器會讓activity的圖標和標籤顯示在應用程序加載器上面,可以讓用戶啓動和返回activity。
第二個能力更爲重要,用戶應該可以在離開一個任務一段時間後返回。因爲這樣,能夠初始化任務的"singleTask"和"singleInstance"模式,只能夠用在那些擁有MAIN
和LAUNCHER 過濾器的activity中。想像一下如果沒有這兩個過濾器會發生什麼:一個行爲啓動了"singleTask"模式的activity,啓動了一個新的任務並且用戶花了一些時間在這個任務上。然後用戶按下了HOME鍵,這個任務被隱藏到了後臺。因爲沒有在應用程序加載器上顯示它,所以就沒有辦法返回到這個任務。
一個類似的麻煩事
FLAG_ACTIVITY_NEW_TASK 標誌。如果這個標誌導致activity啓動了一個新任務,並且用戶按下HOME鍵離開了它,必須有一些方法將用戶引導回它。一些實體(像是通知管理器) 總是在一個外部的任務中啓動activity,而不作爲它們的一部分,所以他們總是將帶有FLAG_ACTIVITY_NEW_TASK 標記的行爲對象傳遞到startActivity() 。如果你有一個可以被外部實體使用這個標籤調用的activity,要注意用戶應該有辦法返回到啓動的任務。
對於那些你不想讓用戶返回到activity的情況,將
<activity>的finishOnTaskLaunch屬性設置爲”true“,參看前面的 清理棧 一節。