Android Activity呼叫棧分析
導語
我們陳述一下Activity,Activity是整個應用使用者互動的核心元件,瞭解Activity的工作模式,生命週期和管理方式,是瞭解Android的基礎。
主要內容
- Activity簡介
- Android任務棧簡介
- AndroidMainifest啟動模式
- Intent Flag啟動模式
- 清空任務棧
- Activity任務棧使用
具體內容
Activity簡介
Activity作為四大組建出現平率最高的元件,我們在哪裡都能看到他,就讓我們一起先來了解一下他的生命週期。
起源
Activity是使用者互動的第一介面,他提供了一個使用者完成指令的視窗,當開發者建立Activity之後,通過呼叫setContentView來指定一個視窗介面,並以此為基礎,提供給使用者互動的介面,系統採用Activity棧的方式來管理Activity。
Activity形態
Activity一個最大的特點就是擁有多種形態,他可以在多種形態中自由切換,以此來控制自己的生命週期。
- Activity/Running:這個時候,Activity處於Activity棧的最頂層,可見,並與使用者進行互動。
- Paused:Activity失去焦點,被一個新的非全屏的Activity或者一個透明的Activity放置在棧頂時,Activity就轉換成了qaused形態,他是去了與使用者互動的能力,所有狀態資訊,成員變數都還保留著,只有在系統記憶體極地的情況下,才會被系統回收。
- Stopped:如果一個Activity被另一個Activity完全覆蓋,那麼Activity就會進入stop形態,此時他不在可見,但依然保留著所有的狀態和成員變數。
- Killed:當Activity被系統回收或者Activity從來沒有建立過,Activity就處於killed狀態。
由此可見,使用者的不同操作,會讓Activity進入四種不同的狀態,而開發者,只能控制其生,卻不能控制其死。
生命週期
Google給了我們一張圖來表示Activity的生命週期,他希望Activity能被開發者所控制,而不是一匹脫繮的野馬。
開發者必然不必實現所有的生命週期方法,但是必須知道每一個生命週期的含義,可以讓我們更好的掌控Activity,讓他能完成你所期望的效果。
Activity啟動和銷燬過程
在系統呼叫onCreate方法之後,就會馬上呼叫onStart,然後繼續呼叫onResume來進圖執行狀態,最後都會停在onResume狀態,完成啟動,系統會呼叫onDestroy來結束一個Activity的生命週期讓他毀掉kill狀態。
以上就是一個Activity的啟動和銷燬的過程。
- onCreate中建立基本的UI元素。
- onPause和onStop:清除Acvtivity的資源,避免浪費。
- onDestroy:因為引用會在Activity銷燬的時候銷燬,而執行緒不會,所以清除開啟的執行緒。
Activity的暫停和恢復過程
當棧頂的Activity部分不可見的時候,就會倒置Activity進入onPause。
- onPause:釋放系統資源。
- onResume:需要重新初始化onPause釋放的資源。
Activity的停止過程
棧頂的Activity部分不可見的時候,實際上後續會有兩種可能,從部分不可見到可見,也就是恢復過程,從部分不可見到完全不可見,也就是停止過程,系統在當前Activity不可見的時候呼叫onPause。
Activity的重新建立過程
最後我們來看看Activity是如何重新建立的,如果你的系統長時間處於stop的狀態,而此時系統需要更多的記憶體或者系統記憶體比較緊張的時候,系統就會回收你的Activity,而系統為了補償你,會將你的Activity狀態通過onRestoreInstanceState()方法儲存到Bundle中去,當然你也可以額外增加鍵值對去儲存這些狀態,當你重新需要建立這個Activity的時候,儲存的Bundle物件就會傳遞到Activity的onRestoreInstanceState()方法中去與onCreate方法中去,這也是onCreate的重要引數——saveInstanceState的來源。
不過這裡要注意的一點就是savedInstanceState方法並不是每次當Activity離開前臺就會呼叫,如果使用者使用finish方法結束,則不會呼叫,而且Android系統已經預設實現了控制元件的快取狀態,一次來減少開發者需要實現的快取邏輯。
Android任務棧簡介
- 當一個App啟動時,如果當前環境下不存在該App的任務棧,那麼系統就會建立一個任務棧,此後,這個App所啟動的Activity都將在這個任務棧中被管理,這個棧也被稱為一個Task,即表示若干個Activity的集合,他們組成在一起形成一個Task,特別要注意的是,一個Task中的Activity可以來自不同的App,同一個App的Activity也可能不在一個Task中。
- 棧的結構是後進先出的線性表,通過在AndroidManifest檔案中的屬性android:launchMode來設定或者是通過Intent的flag來設定的。
AndroidMainifest啟動模式
Android開發者可以在AndroidManifest檔案中一共設計了四種啟動模式:
- standard
- singleTop
- singleTask
- singleInstance
standard
預設的啟動模式,如果不指定Activity的啟動模式,則使用這種模式來啟動Activity,每次點選standard模式建立Activity之後,都會建立新的MainActivity覆蓋在原有的Activity上,如下圖。
singleTop
如果指定Activity的啟動方式為singletop,那麼在啟動的時候,系統會判斷當前棧頂Activity是不是要啟動的那個,如果是則不建立新的Activity,如果不是則建立新的Activity,這種模式通常適用於接收到訊息後顯示的介面,列入QQ接收到訊息後彈出Activity,如果一次來10條,總不能彈10次吧,這種啟動模式的如下圖。
這種啟動模式雖然不能建立新的例項,但是系統任然會在Activity啟動的時候呼叫onNewIntent()方法,舉例子,如果當前任務棧中有ABC三個Activity,而C的啟動模式是singleTop,那麼這個時候再啟動C,那麼系統就不會去建立C的例項了,而是會呼叫C的onNewIntent方法,當前任務棧依然是ABC三個Activity。
singleTask
singleTask模式和singleTop模式有點類似,只不過singleTop是檢測棧頂元素是否需要啟動的Activity,而singleTask是檢測整個Activity棧中是否存在當前啟動的Activity,如果存在,就將他置於棧頂,並且將以上的activity全部銷燬,不過這裡也是指在同一個APP中啟動整個singleTask的Activity,如果是其他的程式以singleTask模式來啟動整個Activity,那麼他將建立一個新的任務棧,不過這裡有一點需要注意的是,如果啟動的模式為singleTask的activity已經在後臺的一個棧中,那麼啟動後,後臺的一個任務棧將一起被切換到前臺,藉助官網的一張圖我們可能更好的理解。
當Activity2啟動ActivityY的時候(啟動模式為singleTask),他所在的task被切換到前臺,且按返回鍵返回的時候,也會先返回ActivityY所在Task的Activity,這一點比較難理解,大家根據圖去研究一下。
可以發現,使用這個模式建立的Activity不是在新的任務棧中被開啟,就是將已開啟的Activity換到前臺,所以這種啟動模式通常可以用來退出整個應用,將主Activity設為singleTask模式,然後在要退出的Activity中轉到主Activity,從而將主Activity之上的Activity全部銷燬,然後重寫主Activity的onNewIntent()方法 在方法中加上一句finish(),將最後一個Activity結束掉。
singleInstance
singieInstance這種啟動模式和使用的瀏覽器工作原理類似。在多個程式中訪問瀏覽器時,如果當前瀏覽器沒有開啟則開啟瀏覽器,否則會在當前開啟的瀏覽器中訪問。申明為singleInstance的Activity會出現在一個新的任務棧中而且該任務棧中只存在這一個Activity,舉個例子來說,如果應用A的任務棧中建立了MainActivity例項,且啟動模式為singleInstance,如果應用B也要啟用MainActivity則不需要建立,兩個應用共享該Activity例項,這種啟動模式常用於需要與程式分離的介面:如在SetupWizard中呼叫緊急呼叫,就是使用這種啟動模式。
關於singleTop和singleInstance這兩種啟動模式還有一點需要特殊說明:如果在一個singleTop或者singleInstance的Activity中通過startActivityForResultO方法來啟動另一個ActivityB, 那麼系統將直接返回Activity_RESULT_CANCELED而不會再去等待返回。這是由於系統在framework層做了對這兩種啟動模式的限制,因為Android開發者認為,不同的Task中,預設是不能傳遞資料的。如果一定要傳遞資料的話,那麼只能通過Intent去繫結資料。
Intent Flag啟動模式
前面就已經說了,系統提供了兩種方式來設定一個Activity的啟動模式,下面要講的是通過Intent設定Flag來設定一個Activity的啟動模式。
常用的Flag:
- Intent.FLAG-ACTIVITY-NEW-TASK:使用一個新的Task來啟動一個Activity,但啟動的每個Aetivity都將在一個新的Task中,該Flag通常使用在從service中啟動的actiity場景,由於在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:使用這種模式啟動Acuvity,當該Activity啟動其他Activity後,該Activity就消失了,不會保留在Activity棧中,例如A-B,B中以這種模式啟動C,C再啟動D,則當前Activity棧為ABD。
清空任務棧
系統同樣提供了清空任務棧的方法來讓我們講一個Task清空,通常情況下,我們可以在activity的標籤上使用以下幾種屬性來清空任務棧。
清空任務棧:
- clearTaskOnLaunch:每次返回Activity的時候,都將該Activity上的所有Activity都清除,通過這個屬性,可以讓這個Task每次初始化的時候,都只有一個Activity。
- finishTaskOnLaunch:這個屬性和clearTaskOnLaunch有點類似,只不過clearTaskOnLaunch作用在別人身上,而finishTaskOnLaunch作用在自己身上,通過這個屬性,當離開這個Activity所處的Task,那麼使用者再返回的時候,該Activity會被finish掉。
- alwaysRetainTaskState:Task的一道免死金牌,如果將Activity這個屬性設定為true,那麼該Activity所在的Task將不接受任何清除命令,一直保持當前Task的狀態。
Activity任務棧使用
我們使用Activity任務棧的各種啟動模式和清理方法,是為了更好地使用App中Activity,合理地設定Activity的啟動模式會讓程式執行更有效率,使用者體驗更好。但任務棧雖好,卻也不能濫用,如果過度地使用Activity任務棧,則會導致整個App的棧管理混亂,不利於以後程式的拓展,而且在容易出現由於任務棧導致的顯示異常,這樣的bug是很難調的。所以,在App中使用Activity任務棧一定要根據實際專案的需要,而不是為了使用任務棧而使用任務棧。
總結
- 理解Activity的生命週期與工作模式。
- 理解Activity呼叫棧管理。