Android系統程式設計入門系列之載入介面Activity
上回說到應用初始化載入及其生命週期,在Android系統呼叫Applicaiton.onCreate()
之後,繼續建立並載入清單檔案中註冊的首個介面即主Activity
,也可稱之為入口介面。主Activity
的確定規則在Android系統程式設計入門系列之清單檔案有介紹,本文主要介紹Android系統建立Activity
之後的生命週期流程。
在清單檔案中所註冊的介面均為自定義Activity
,其父類往上追溯,必須繼承自android.content.Activity。
生命週期
Activity
作為四大元件之首,主要負責與系統使用者的視覺化互動響應。只有深刻掌握Activity
的生命週期及相關概念,才能在開發設計時遊刃有餘。注意,這裡的生命週期介紹,與
Android元件的生命週期,均是由Android系統主執行緒呼叫,如果在呼叫的生命週期方法內出現耗時操作,將會導致後續的生命週期方法無法被及時呼叫,反應到互動介面上就是應用程式卡頓甚至操作無響應。為了防止這種情況的發生,Android系統定義當應用程式超時無響應時間超過一定時長(介面Activity
預設5秒),會觸發應用無響應錯誤ANR,同時介面彈出提示對話方塊,以供使用者選擇退出停止響應或繼續等待。所以在生命週期方法內不允許耗時操作。
(呼叫構造方法)啟動例項化
介面Activity
的啟動需要通過android.content.Intent
Activity
的具體包名和類名,而隱示意圖只需要指定Activity
在清單檔案中註冊時所嵌入的action和category標籤資訊。建立的意圖作為引數才能啟動介面Activity
。顯示意圖常用於當前應用內的介面Activity
之間啟動,而隱示意圖多用於不同應用間的介面啟動。
回想下,在Android系統程式設計入門系列之清單檔案文章中說到,主Activity
的註冊時,必須在其標籤內部嵌入<intent-filter></intent-filter>
標籤,並在該標籤內固定且唯一<action>
<category>
標籤的內容。這種寫法的原因,就在於Android系統是建立的隱示意圖啟動應用內的主Activity
。舉一反三,留個問題思考下,如果一個應用的清單檔案中有多個上述主Activity
的固定<intent-filter></intent-filter>
標籤內容,那Android系統在啟動這個應用後會怎麼呼叫這些Activity
呢?(答案將在相應視訊教程中揭曉)
在Android系統通過清單檔案找到意圖指定的介面Activity
之後,會例項化該介面Activity
物件,並將其放入當前應用程式的指定任務棧中。任務棧,正如其名,是採用先進後出的模式管理當前應用程式啟動的所有介面Activity
物件的資料結構。
當介面Activity
啟動時放入任務棧中,退出介面Activity
時則從任務棧中取出其物件並銷燬。然而如果一個應用程式只對應一個任務棧,在有些頻繁啟動退出單個介面的場景中,就會頻繁建立銷燬該介面例項,浪費了很多cpu耗時。為了優化介面的例項化過程,Android系統允許一個應用程式使用多個任務棧。這也就引出了兩個問題,一是如何從多個任務棧中指定具體一個作為介面啟動管理的任務棧?二是介面在啟動時放入不同任務棧的規則是什麼?
對於第一個問題,在清單檔案中靜態註冊介面Activity
時,可以在<activity></activity>
標籤中對屬性名taskAffinity賦值,該屬性值預設為當前應用包名,從而指定當前介面Activity
所屬的任務棧。而<application></application>
標籤中也可以使用該屬性,表示當前應用程式內的所有介面啟動時都使用該屬性值對應的任務棧管理。同時在介面Activity
中,可以使用getTaskId()
來獲取當前介面Activity
所屬任務棧對應的id值。
對於第二個問題,可以根據介面Activity
的啟動模式來確定放入任務棧的規則。啟動模式可以在清單檔案中靜態註冊<activity></activity>
標籤時,指定其屬性launchMode賦值;也可以在建立意圖Intent
操作之後,通過呼叫其setFlags(int flags)
方法動態設定。如果動態設定方式會優先覆蓋靜態設定方式,如果不設定啟動模式,則使用預設模式啟動。啟動模式主要有四種,其具體方式和對應的屬性值可參考下表。
啟動模式 | 靜態設定 | 動態設定 | 功能含義 |
---|---|---|---|
標準模式(預設模式) | standard | - | 啟動時會在記憶體空間中例項化物件,並將該物件放入指定任務棧中 |
棧頂單例模式 | singleTop | Intent.FLAG_ACTIVITY_SINGLE_TOP | 啟動時先檢查指定任務棧頂部介面物件, 如果與該介面資訊一致,則使用任務棧頂層的物件; 否則例項化物件並放入指定任務棧頂部 |
棧內單例模式 | singleTask | Intent.FLAG_ACTIVITY_CLEAR_TOP | 啟動時檢查指定任務棧內的所有介面物件, 如果存在與該介面資訊一致的物件,則將其頂部介面物件移出任務棧並銷燬,最終保留一致物件在任務棧頂部; 否則例項化物件並放入指定任務棧頂部 |
全域性單例模式 | singleInstance | Intent.FLAG_ACTIVITY_NEW_TASK | 啟動時檢查當前應用程式使用的所有任務棧內的所有介面物件, 如果存在與該介面資訊一致的物件,則將其所在任務棧頂部的介面物件移出任務棧並銷燬,最終保留一致物件在任務棧頂部; 否則例項化物件並放入指定任務棧頂部 |
在介面`Activity`啟動例項化階段,只是建立例項,並未載入Android系統相關環境,故該階段一般不需要重寫構造方法。
(呼叫attachBaseContext(Context base)
)載入執行環境
與Application
一樣,Activity
也繼承自android.content.ContentWrapper
,使用時繫結上下文環境,尤為注意的是,在AndroidSDK定義的所有android.content.ContentWrapper
子類中,只有android.content.Activity
類中重寫了attachBaseContext(Context base)
方法。故介面Activity
例項化之後,系統會最終呼叫android.content.Activity.attachBaseContext(Context base)
方法,在該方法中以完成將當前介面與上下文環境繫結的任務。
同樣的,在介面Activity
載入執行環境階段,也不推薦重寫該方法,介面內所有的操作都應在該方法之後完成。
(呼叫onCreate()
)介面建立
在介面載入執行環境之後,Android系統對介面的建立工作就完成了。之後系統回撥onCreate()
方法,官方稱之為已建立狀態。此時當前Activity
還處在後臺未展示給使用者,因此可以重寫該方法,以完成介面內相關變數的初始化操作。
(呼叫onStart()
)介面展示
在介面建立之後,或介面重新展示之後,系統會回撥onStart()
方法,將當前Activity
繪製給使用者可見,官方稱之為已開始狀態。如果重寫該方法,可以完成展示給使用者的介面初始化操作。
(呼叫onResume()
)介面執行
當介面展示之後,系統會回撥onResume()
方法,使得介面可以與使用者進行互動,官方稱之為已恢復狀態。此時系統開始響應該介面內的使用者互動,這也表明當前介面Activity
已經處於運行了。
系統呼叫該生命週期方法後會一直處於介面執行中,直到使用者的一些操作改變該介面的狀態。這些使用者操作包括但不限於:
- 在該介面
Activity
啟動另一個介面Activity
。 - 點選back返回按鍵,退出該介面
Activity
,返回上一個介面Activity
。 - 點選home主頁按鍵,該應用
Application
切換到後臺。 - 點選menu選單按鍵,該應用的當前介面
Activity
在最近工作列展示。 - 點選分屏按鍵,該應用
Application
作為多視窗模式之一顯示。 - 點選息屏按鍵,該應用
Application
在後臺休眠。 - 點選電源按鍵,強制關機,該應用
Application
被殺死。
介面暫停(呼叫onPause()
)
當介面執行狀態被改變後,系統都會首先呼叫onPause()
方法,此時系統已停止當前介面Activity
與使用者的互動響應操作,官方稱之為已暫停狀態。可以重寫該方法,在其中做釋放一些不需要但卻很耗電的系統資源,以防止無效持有消耗電量。但是此時該介面仍然對使用者可見,所以不建議在該方法內執行耗時的釋放操作,以免給使用者帶來卡頓的假象。
在該狀態之後,Android系統根據之前的使用者操作型別判斷後續生命週期流程。
操作1、2、3、6、7可能會繼續令當前介面Activity
停止展示。
而操作4、5將可能在使用者重新點選該介面返回,此時將重新回到介面執行的生命週期中。
介面停止展示(呼叫onStop()
)
當系統令當前介面Activiy
停止展示時,會呼叫onStop()
方法,將當前介面對使用者不可見,官方稱之為已停止狀態。重寫該方法時,可以釋放介面資源和不需要的CPU耗時資源,以供其他地方及時獲得。
在該狀態之後,Android系統繼續根據之前的使用者操作型別判斷後續的生命週期流程。
操作1會將新啟動的介面Activity
放入當前Activity
所在任務棧中,等待新啟動的介面返回時,令當前介面重新展示。
操作2則是將當前介面從任務棧中取出並銷燬。
操作3則可能在手機記憶體不足時,將當前應用的所有介面包括當前介面從任務棧中取出並銷燬。
操作6會將當前應用休眠,等裝置重新亮屏後,令當前介面重新展示;也可能有的裝置在息屏休眠後保持低消耗電源模式時,當前應用超過一定時間或記憶體佔用的休眠期後,被系統殺死,即當前應用的所有介面包括當前介面從任務棧彙總取出並銷燬。
操作7則會將當前應用的所有介面全部銷燬,不會再呼叫當前介面的任何生命週期。
介面重新展示(呼叫onRestart()
)
當介面由使用者不可見轉為可見時,如果之前已經介面建立了,則會呼叫onRestart()
方法,以表明當前介面將會重新展示。重寫該方法可以處理對使用者重新可見後的操作。
該方法呼叫之後,系統將會繼續呼叫介面展示和執行,以重新執行介面與使用者的互動操作。
介面銷燬(呼叫onDestroy()
)
當介面Activity
被移除任務棧後,如果應用程式還處在Android系統的控制下,系統將在呼叫onDestroy()
方法之後銷燬介面,官方稱之為已銷燬狀態。重寫此方法可以釋放所有不需要的資源,以防止發生記憶體洩漏OOM的問題。
上述內容是針對單個介面Activity
的載入流程,那麼其中涉及到的兩個介面Activity
的相互交流方式是怎樣的,以及介面執行之後如何處理與使用者的互動操作?詳情請關注後續文章。