任務棧Task,是一種用來放置Activity實例的容器
他是以棧的形式進行盛放,也就是所謂的先進后出,主要有2個基本操作:壓棧和出棧,其所存放的Activity是不支持重新排序的,只能根據壓棧和出棧操作更改Activity的順序。
啟動一個application的時候,系統會為它默認創建一個對應的Task,用來放置根Activity。
默認啟動Activity會放在同一個Task中,新啟動的Activity會被壓入啟動它的那個Activity的棧中,并且顯示它
當用戶按下回退鍵時,這個Activity就會被彈出棧,按下Home鍵回到桌面,再啟動另一個應用,這時候之前那個Task就被移到后臺,成為后臺任務棧,而剛啟動的那個Task就被調到前臺,成為前臺任務棧,Android系統顯示的就是前臺任務棧中的Top實例Activity
以往基于應用(application)的程序開發中,程序具有明確的邊界,一個程序就是一個應用,一個應用為了實現功能可以采用開辟新線程甚至新進程來輔助,但是應用與應用之間不能復用資源和功能。
而Android引入了基于組件開發的軟件架構,雖然我們開發android程序,仍然使用一個apk工程一個Application的開發形式,但是對于Aplication的開發就用到了Activity、service等四大組件,其中的每一個組件,都是可以被跨應用復用的,這就是android的神奇之處。
雖然組件可以跨應用被調用,但是一個組件所在的進程必須是在組件所在的Aplication進程中。由于android強化了組件概念,弱化了Aplication的概念,所以在android程序開發中,A應用的A組件想要使用拍照或錄像的功能就可以不用去針對Camera類進行開發,直接調用系統自帶的攝像頭應用(稱其B應用)中的組件(稱其B組件)就可以了
但是這就引發了一個新問題,A組件運行在A應用中,B組件運行在B應用中,自然都不在同一個進程中,那么從B組件中返回的時候,如何實現正確返回到A組件呢?Task就是來負責實現這個功能的,它是從用戶角度來理解應用而建立的一個抽象概念。因為用戶所能看到的組件就是Activity,所以Task可以理解為實現一個功能而負責管理所有用到的Activity實例的棧
棧是一個先進后出的線性表,根據Activity在當前棧結構中的位置,來決定該Activity的狀態。正常情況下,當一個Activity啟動了另一個Activity的時候,新啟動的Activity就會置于任務棧的頂端,并處于活動狀態,而啟動它的Activity雖然成功身退,但依然保留在任務棧中,處于停止狀態,當用戶按下返回鍵或者調用finish()方法時,系統會移除頂部Activity,讓后面的Activity恢復活動狀態。
當然,世界不可能一直這么和諧,可以給 Activity 一些設置來打破這種“和諧”的模式,這種特權,就是通過在AndroidManifest文件中的屬性andorid:launchMode來設置或者通過Intent的flag來設置的,下面就先介紹下Activity的幾種啟動模式
默認模式,可以不用寫配置。在這個模式下,都會默認創建一個新的實例。因此,在這種模式下,可以有多個相同的實例,也允許多個相同Activity疊加。應用場景:絕大多數Activity
如果以這種方式啟動的Activity被跨進程調用,在5.0之前新啟動的Activity實例會放入發送Intent的Task的棧的頂部,盡管它們屬于不同的程序,這似乎有點費解看起來也不是那么合理,所以在5.0之后,上述情景會創建一個新的Task,新啟動的Activity就會放入剛創建的Task中,這樣就合理的多了
如果要開啟的activity在任務棧的頂部已經存在,就不會創建新的實例,而是調用 onNewIntent() 方法。避免棧頂的activity被重復的創建
應用場景:在通知欄點擊收到的通知,然后需要啟動一個Activity,這個Activity就可以用singleTop,否則每次點擊都會新建一個Activity。當然實際的開發過程中,測試妹紙沒準給你提過這樣的bug:某個場景下連續快速點擊,啟動了兩個Activity。如果這個時候待啟動的Activity使用 singleTop模式也是可以避免這個Bug的
同standard模式,如果是外部程序啟動singleTop的Activity,在Android 5.0之前新創建的Activity會位于調用者的Task中,5.0及以后會放入新的Task中
activity只會在任務棧里面存在一個實例。如果要激活的activity,在任務棧里面已經存在,就不會創建新的activity,而是復用這個已經存在的activity,調用 onNewIntent() 方法,并且清空這個activity任務棧上面所有的activity
應用場景:大多數App的主頁。對于大部分應用,當我們在主界面點擊回退按鈕的時候都是退出應用,那么當我們第一次進入主界面之后,主界面位于棧底,以后不管我們打開了多少個Activity,只要我們再次回到主界面,都應該使用將主界面Activity上所有的Activity移除的方式來讓主界面Activity處于棧頂,而不是往棧頂新加一個主界面Activity的實例,通過這種方式能夠保證退出應用時所有的Activity都能報銷毀
在跨應用Intent傳遞時,如果系統中不存在singleTask Activity的實例,那么將創建一個新的Task,然后創建SingleTask Activity的實例,將其放入新的Task中
單一實例模式,整個手機操作系統里面只有一個實例存在。不同的應用去打開這個activity 共享公用的同一個activity。他會運行在自己單獨,獨立的任務棧里面,并且任務棧里面只有他一個實例存在。
應用場景:呼叫來電界面。這種模式的使用情況比較罕見,在Launcher中可能使用。或者你確定你需要使Activity只有一個實例。建議謹慎使用
系統提供了兩種方式來設置一個Activity的啟動模式,除了在AndroidManifest文件中設置以外,還可以通過Intent的Flag來設置一個Activity的啟動模式,下面我們在簡單介紹下一些Flag
FLAG_ACTIVITY_NEW_TASK 使用一個新的Task來啟動一個Activity,但啟動的每個Activity都講在一個新的Task中。該Flag通常使用在從Service中啟動Activity的場景,由于Service中并不存在Activity棧,所以使用該Flag來創建一個新的Activity棧,并創建新的Activity實例FLAG_ACTIVITY_SINGLE_TOP 使用singletop模式啟動一個Activity,與指定android:launchMode=“singleTop”效果相同FLAG_ACTIVITY_CLEAR_TOP 使用SingleTask模式來啟動一個Activity,與指定android:launchMode=“singleTask”效果相同FLAG_ACTIVITY_NO_HISTORY Activity使用這種模式啟動Activity,當該Activity啟動其他Activity后,該Activity就消失了,不會保留在Activity棧中我們在開發過程中經常會用到 StartActivityForResult方法啟動一個Activity,然后在onActivityResult()方法中可以接收到上個頁面的回傳值
但你有可能遇到過拿不到返回值的情況,那有可能是因為Activity的LaunchMode設置為了singleTask。
5.0之后,android的LaunchMode與StartActivityForResult的關系發生了一些改變。兩個Activity,A和B,現在由A頁面跳轉到B頁面,看一下LaunchMode與StartActivityForResult之間的關系:

這是因為ActivityStackSupervisor類中的startActivityUncheckedLocked方法在5.0中進行了修改。
在5.0之前,當啟動一個Activity時,系統將首先檢查Activity的launchMode,如果為A頁面設置為SingleInstance或者B頁面設置為singleTask或者singleInstance,則會在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK標志,而如果含有FLAG_ACTIVITY_NEW_TASK標志的話,onActivityResult將會立即接收到一個cancle的信息
而5.0之后這個方法做了修改,修改之后即便啟動的頁面設置launchMode為singleTask或singleInstance,onActivityResult依舊可以正常工作,也就是說無論設置哪種啟動方式,StartActivityForResult和onActivityResult()這一組合都是有效的。所以如果你目前正好基于5.0做相關開發,不要忘了向下兼容,這里有個坑請注意避讓
新聞熱點
疑難解答