Android系統編程入門系列之載入介面Activity
- 2021 年 7 月 2 日
- 筆記
- Android_Home
上回說到應用初始化載入及其生命周期,在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
的相互交流方式是怎樣的,以及介面運行之後如何處理與用戶的交互操作?詳情請關注後續文章。