本節書摘來自異步社群《android開發進階:從小工到專家》一書中的第1章,第1.1節activity,作者 何紅輝,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視
第1章 android的構成基石—四大元件
android開發進階:從小工到專家
由于本書的目标讀者是有一定android基礎的開發人員,是以,本章不再介紹android系統的架構、曆史等知識,而是直接切入主題,從講解android的四大元件開始,然後一步一步深入學習開發中的重要知識點,使得我們能夠從基本原理層面掌握android開發基礎知識。
android中最重要的是四大元件,即activity、service、contentprovider和broadcast。這4個元件分工明确,共同構成了可重用、靈活、低耦合的android系統。activity負責ui元素的加載與頁面之間的跳轉,代表了一個頁面單元;service負責與ui無關的工作,如在背景執行耗時操作等;contentprovider負責存儲、共享資料,使得資料可以在多個應用之間共享;broadcast則是在各個元件、應用之間進行通信,簡化了android開發中的通信問題。
下面就來簡單學習一下這四大開發元件。
1.1 activity
activity在應用中的表現就是一個使用者界面,它會加載指定的布局檔案來顯示各種ui元素,例如textview、button、imageview、listview等,并且為這些ui元素設定事件處理函數,使得使用者可以與這些ui進行互動。同時,activity還可以在不同的activity之間跳轉,将不同的頁面串連在一起,共同完成特定的操作流程。每個應用都是由一個或者多個activity組成,它是android應用程式中不可缺少的部分。
應用啟動時會加載一個預設的activity,這個activity在androidmanifest.xml中會被設定為如下intent-filter:
每個activity都有生命周期,在不同的階段會回調不同的生命周期函數,activity的生命周期函數有如下幾個。
1.oncreate()
相信這是開發者見過次數最多的函數,我們在建立繼承自activity的類時都會預設生成這個函數。它會在activity第一次被建立時調用,通常會在這個函數中完成activity的初始化操作,如設定布局、初始化視圖、綁定事件等。
2.onstart()
這個函數在activity的oncreate函數調用之後被調用,此時的activity還處在不可見狀态,它的下一個狀态就是activity變得可見的時候,也就是這個函數在activity可見之前被調用。
3.onresume()
這個函數在activity變為可見時被調用,執行完onresume之後,activity就會請求ams渲染它所管理的視圖。此時的activity一定位于傳回棧的棧頂,并且處于運作狀态。
4.onpause()
這個函數在系統準備去啟動或者恢複另一個activity時調用,也就是在activity即将從可見狀态變為不可見時。我們通常會在這個函數中将一些消耗cpu的資源釋放掉,以及儲存一些關鍵資料。
5.onstop()
這個函數在activity完全不可見時調用。它和onpause()函數的主要差別在于,如果新啟動的activity是一個對話框式的activity,那麼onpause()函數會得到執行,而onstop() 函數并不會執行。
6.ondestroy()
這個函數在activity被銷毀之前調用,之後activity的狀态将變為銷毀狀态。
7.onrestart()
這個函數在activity由停止狀态重新變為運作狀态之前調用,也就是activity被重新啟動了。
從oncreate()函數到ondestroy()函數運作的時期就是一個activity的完整生命周期。一般情況下。我們會在一個activity的oncreate()函數中完成各種初始化操作,而在ondestroy()函數中完成釋放記憶體的操作。然而并不是各個時期activity都是可見的,隻有onresume()函數和onstop()函數之間的activity是可見的,在activity可見期内,使用者可以與activity進行互動,完成所需的功能。
為了幫助讀者能夠更好地了解,android 官方提供了一個activity生命周期的示意圖,如圖1-1所示。

1.1.1 activity的構成
activity的構成并不是一個activity對象再加上一個布局檔案那麼簡單,在activity和開發人員設定的視圖之間還隔着兩層。實際上視圖會被設定給一個window類,這個window中含有一個decorview,這個decorview才是整個視窗的頂級視圖。開發人員設定的布局會被設定到這個decorview的mcontentparent布局中。也就是說android中實際上内置了一些系統布局檔案xml,我們在xml中定義的視圖最終會被設定到這些系統布局的特定節點之下,這樣就形成了整個decorview。結構如圖1-2所示。
從圖1-2中可以看到,我們的activity之下有一個phonewindow,這個phonewindow是window的實作類,然後window之下包含一個decorview,decorview實際上是頁面的頂級視圖,它從一些系統布局中加載,并且在運作時将開發人員設定給activity的布局資源添加到系統布局的mcontentparent中。這樣一來,使用者界面就被添加到系統布局中了,而系統布局會為我們設定好标題欄區域等。
下面就是一個名為screen_title的系統布局xml檔案:
上述xml檔案中包含了actionbar和标題欄區域,下面就是開發人員設定給activity的布局區域,這個區域被添加到名為content的布局中,而這整個screen_title.xml又是decorview的子視圖,是以,最終使用者界面會顯示為标題欄、開發人員設定的界面。例如我們的activity布局代碼如下:
該布局的根視圖為relativelayout,其中隻有一個居中的textview。運作後的界面如圖1-3所示。
jtm_chap01顯示的區域就是id為title的textview,而hello world就是content布局下的一個子視圖。當activity的onresume函數被調用之後,使用者界面就顯示在我們面前了。
1.1.2 activity的4種啟動模式
每個應用程式都是由一個或者多個activity組成,是以,android内部使用通過回退棧來管理activity執行個體。棧是一種後進先出的集合,對于android來說,目前顯示的activity就在棧頂,當使用者點選後退鍵或者點選應用上的傳回按鈕,系統就會将棧頂的activity出棧,此時原來棧頂下的activity就會變為棧頂顯示到裝置上。
然而事情可能并不是那麼簡單,在一些特殊情況下我們可能需要對activity執行個體做一些特殊的處理,例如,為了避免重複建立activity,我們要求一個activity隻有一個執行個體。好在android系統為我們提供了這些功能,也就是我們本節要說的activity的4個啟動模式。使用者可以在androidmanifext.xml注冊activity時設定它的啟動模式,例如:
activity的啟動模式有4個,分别為standard、singletop、singletask、singleinstance,下面我們逐個介紹它們。
1.standard(标準啟動模式)
這是activity的标準啟動模式,也是activity的預設啟動模式。在這種模式下啟動的activity可以被多次執行個體化,即在同一個任務棧中可以存在多個activity執行個體,每個執行個體都會處理一個intent對象。如果activitya的啟動模式為standard,并且已經有一個activitya被啟動,在該activitya中調用startactivity時會啟動一個新的activitya執行個體。棧的變化如圖1-4所示。
如果activitya是一個非常耗資源的類,那麼将會使它所依附的應用消耗更多的系統資源。
2.singletop
如果一個以singletop模式啟動的activity的執行個體已經存在于任務棧的棧頂,那麼再啟動這個activity時,不會建立新的執行個體,而是重用位于棧頂的那個執行個體,并且會調用該執行個體的onnewintent()函數将intent對象傳遞到這個執行個體中。例如,activitya的啟動模式為singletop,并且activitya的一個執行個體已經存在于棧頂中。那麼再調用startactivity啟動另一個activitya時,不會再次建立activitya的執行個體,而是重用原來的執行個體,并且調用原來執行個體的onnewintent()函數。此時任務桟中還是這一個activitya的執行個體。棧内變化如圖1-5所示。
如果以singletop模式啟動的activity的一個執行個體已經存在于任務桟中,但是不在桟頂,那麼它的行為和standard模式相同也會建立一個新的執行個體。棧内變化如圖1-6所示。
3.singletask
singletask模式是常用的啟動模式,如果一個activity設定了該啟動模式,那麼在一個任務棧中隻能有一個該activity的執行個體。如果任務棧中還沒有該activity,會新建立一個執行個體并放在棧頂。但是,如果已經存在activity,系統會銷毀處在該 activity上的所有activity,最終讓該 activity執行個體處于棧頂。最終讓該 activity執行個體處于棧頂,同時回調該activity的onnewintent()函數。棧内變化如圖1-7所示。
4.singleinstance
設定了singleinstance模式的activity會在一個獨立的任務中開啟,并且這個新的任務中有且隻有這一個執行個體,也就是說被該執行個體啟動的其他activity會自動運作于另一個任務中。當再次啟動該activity執行個體時,會重用已存在的任務和執行個體。并且會調用該執行個體的onnewintent()函數,将intent執行個體傳遞到該執行個體中。
和singletask不同的是,同一時刻在系統中隻會存在一個這樣的activity執行個體,而singletask模式的activity是可以有多個執行個體的,隻要這些activity在不同的任務棧中即可,例如,應用a啟動了一個啟動模式為singletask的activitya,應用b又通過intent想要啟動一個activitya,此時由于應用a和應用b都有自己的任務棧,是以,在這兩個任務棧中分别都有一個activitya示例。而singleinstance能夠保證activity在系統中隻有一個執行個體,不管多少應用要啟動該activity,這個activity有且隻有一個,如圖1-8所示。
1.1.3 fragmentactivity與fragment
為了更好地運用越來越大的螢幕控件,android在3.0版本引入了fragment,它可以像activity一樣包含布局。不同的是fragment是被嵌套在activity中使用,它作為一個更大粒度的ui單元。如果需要相容低于android 3.0的系統,那麼開發人員需要引用android-support-v4的jar包才能使用fragment功能。
假如有這樣的場景:我們的新聞應用含有兩個activity,第一個activity是顯示新聞标題、概要資訊的清單,當使用者點選這些标題時進入該新聞的詳情頁面進行閱讀。
這是一個再普通不過的場景,但是這樣做真的合适嗎?我們是否能夠簡化使用者的操作?
答案是:必須的!
fragment就是為了應對這種情況而出現的,我們可以使用兩個fragment,fragment1包含了一個新聞标題的清單,每行顯示一個新聞的标題;fragment2則展示這條新聞的詳細内容。如果現在程式運作在豎屏模式的平闆電腦或手機上,fragment 1可能嵌入在一個activity中,而fragment 2可能嵌入在另一個activity中,如圖1-9所示。
而如果現在程式運作在橫屏模式的平闆電腦上,兩個fragment就可以嵌入在同一個activity中,如圖1-10所示。
就目前開發來說,使用fragment已經成為流行的開發方式,盡管在它的support v4中存在各種各樣的bug,以至于square這樣的公司舉起了聲讨fragment的大旗,但是也不能阻止fragment“馳騁”在大螢幕手機盛行的時代。