Activity的生命週期,對於Android開發者來說,再熟悉不過了。但是我們接觸到的資料,絕大部分都只是談了一些表面上的東西,例如各個回撥的順序等等。本文試圖換個角度來講解,也希望對各位讀者有所幫助。
生命週期
首先附上一張大家都熟悉的不能再熟悉的圖了
對於各個流程的回撥,想必大家早已熟記於心了,對於單個Activity來說,完全沒問題,複雜點的,不就是轉屏嘛?能有啥,好的,下面幾個問題,麻煩大家先思考下。
- FirstActivity啟動了SecondActivity,整個流程是怎樣的?
- setContentView如果放在onStart或者onResume中,會有什麼問題嗎?
- onPause中可以儲存狀態,為什麼還要onSaveInstanceState,onCreate中有恢復機制,為什麼還需要onRestoreInstanceState?
- 如何判斷一個Activity真正可見
來自官方
在回答這些問題之前,先來回顧下Activity的各個階段,下面的英文部分出自Google Android官方文件。
onCreate
Called when the activity is first created. This is where you should do all of your normal static set up: create views, bind data to lists, etc. This method also provides you with a Bundle containing the activity's previously frozen state, if there was one. Always followed by onStart().
直接看重點,onCreate是用來幹啥的,建立view、繫結data的地方。是初始化Activity的地方,setContentView以及獲取控制元件都應該放在這裡去做。
onStart
Called when the activity is becoming visible to the user. Followed by onResume() if the activity comes to the foreground, or onStop() if it becomes hidden.
onPause會被呼叫,是在Activity正在對使用者變得可見的時候。也就是說這個時候,對於使用者來說不是真正的可見,也不可去響應使用者的輸入。
onResume
Called when the activity will start interacting with the user. At this point your activity is at the top of the activity stack, with user input going to it. Always followed by onPause().
onResume是在將要可以產生互動的時候被呼叫的,也就是說,還不能響應使用者的輸入操作。在這個階段,Activity已經是處在棧頂了。
onPause
Called when the system is about to start resuming a previous activity. This is typically used to commit unsaved changes to persistent data, stop animations and other things that may be consuming CPU, etc. Implementations of this method must be very quick because the next activity will not be resumed until this method returns. Followed by either onResume() if the activity returns back to the front, or onStop() if it becomes invisible to the user.
onPause可以用來幹啥,停止動畫或者那些佔用CPU操作的地方,但是在onPause裡面進行的操作,不能夠太久,應該very quick,因為下一個Activity需要等onPause結束後才會被調起。這個very quick的時間,應該是多少了?這個在ActivityManagerService中有定義,不能超過500ms。如果在onPause裡面處理時間超過5秒的話,會不會出現ANR呢?
// How long we wait until giving up on the last activity to pause. This
// is short because it directly impacts the responsiveness of starting the
// next activity.
static final int PAUSE_TIMEOUT = 500;
複製程式碼
onStop
Called when the activity is no longer visible to the user, because another activity has been resumed and is covering this one. This may happen either because a new activity is being started, an existing one is being brought in front of this one, or this one is being destroyed. Followed by either onRestart() if this activity is coming back to interact with the user, or onDestroy() if this activity is going away.
onStop的描述很簡單,Activity對使用者不可見時呼叫,但是文件後面Killable一欄顯示的是YES,也就是說,onStop有可能會被強制結束掉而不完整的執行。
onDestroy
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
onDestroy是在Activity要被釋放掉時呼叫的,但是這個被釋放,有主動的(手動去呼叫finish())和被動的(系統回收),可以通過isFinishing來區分這兩種場景。
onSaveInstanceState
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state.
The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)).
If called, this method will occur after onStop() for applications targeting platforms starting with P. For applications targeting earlier platform versions this method will occur before onStop() and there are no guarantees about whether it will occur before or after onPause().
從官網的三段介紹,可以看出幾點
首先,onSaveInstanceState的呼叫時機,它是在當前Activity可能會被殺死的時候才會觸發,什麼場景下會被殺死呢?當前Activity存在被回收的可能,就可能會被系統殺死。如果使用者主動的去殺死當前Activity,這個方法是不會被呼叫的。
其次,它的作用主要是用於儲存UI層面的狀態,不同於onPause。
最後,這個方法的呼叫時機,在不同的系統中不同,從Android P開始,它是在onStop之後呼叫的,在之前的系統中,則是在onStop之前呼叫的。但是是否發生在onPause前後,則看具體情況。
onRestoreInstanceState
This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState. Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation. The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle).
This method is called between onStart() and onPostCreate(Bundle).
可以看出,這個方法的主要作用,是恢復在onSaveInstanceState中儲存的狀態。它的呼叫時機在onStart和onPostCreate之間。
回到問題
讀了谷歌官方文件,是否會發現一些平時開發中沒有注意到的點呢?接下來我們回到上面的幾個問題。
FirstActivity啟動了SecondActivity,整個流程是怎樣的?
在正常情況下,按照文件說的,首先會去呼叫FirstActivity的onPause方法,在onPause方法結束完畢後,然後呼叫SecondActivity的onCreate、onStart、onResume,最後是FirstActivity的onStop。這個是根據文件來推斷的,我們實際跑一下程式。
I/FirstActivity: =====FirstActivity=====onPause
I/FirstActivity: =====FirstActivity=====onWindowFocusChanged
I/SecondActivity: =====SecondActivity=====onCreate
I/SecondActivity: =====SecondActivity=====onStart
I/SecondActivity: =====SecondActivity=====onResume
I/SecondActivity: =====SecondActivity=====onWindowFocusChanged
複製程式碼
從列印的log可以看出,跟推斷的一樣,首先呼叫的是FirstActivity的onPause,然後才是resume SecondActivity。
setContentView如果放在onStart或者onResume中,會有什麼問題嗎?
從官方文件來看,也只是說應該把setContentView放在onCreate中,並沒有說必須放在這裡,所以應該也不會有顯示以及呼叫的問題吧。還是跑一下程式看看,分別將setContentView以及設定點選事件放在onStart以及onResume中,跑了下程式,沒有出現顯示問題。但是這個僅僅是顯示上的問題,會不會存在效率的問題呢?我們來列印一下時間,以呼叫onCreate到onWindowFocusChanged之間的時間,作為Activity載入時間,來進行對比
I/SecondActivity: =====SecondActivity=====Load Time:56
I/SecondActivity: =====SecondActivity=====Load Time:57
I/SecondActivity: =====SecondActivity=====Load Time:57
複製程式碼
三次時間幾乎一樣的,也就是說,如果只是單叢初次啟動的效率來說,在三個地方去進行setContentView是沒有任何差別的。但是為甚麼官方說應該放在onCreate裡面去處理了,這是因為,onCreate在正常狀況下,只會被呼叫一次,而onStart以及onResume都會被呼叫多次,放在這裡面去做的話,在onResume的過程,會增加額外的耗時。
另外,由於onRestoreInstanceState是在onStart之後才呼叫的,如果將setContentView放在onResume的話,可能會產生問題。
onPause中可以儲存狀態,為什麼還要onSaveInstanceState,onCreate中有恢復機制,為什麼還需要onRestoreInstanceState?
首先,onSaveInstanceState以及onRestoreInstanceState是Android進行UI狀態儲存與恢復的一套單獨的機制。說是單獨的機制,是因為每個view裡面,都會有onSaveInstanceState去進行一些預設的狀態儲存操作,常規狀態下不需要使用者去幹預,比方說編輯框中輸入的文字資訊,這樣做為開發者省了很多事。
根據官方文件來看,onPause主要的作用是停止動畫以及一些耗CPU的操作,可以用於儲存狀態。onSaveInstanceState主要作用是對UI進行狀態儲存。兩者的側重點不同,onPause也可以儲存UI的狀態,但是,onPause設計的主要目的是和onStart配對,停止耗時操作。
onCreate中也可以恢復狀態,但是onRestoreInstanceState的觸發時間滯後於onCreate,在onStart之後執行,它設計的目的是用來恢復onSaveInstanceState中儲存的狀態。
onPause以及onCreate可以幹這些事情,但是它們當初不是設計出來,專門幹這個事情的。
如何判斷一個Activity真正可見?
對於這個問題,其實沒有嚴格的說法,onResume以及onWindowFocusChanged中都可以做判斷,但是官方文件上說,onWindowFocusChanged是Activity對使用者是否可見最好的指示器。
擴充套件之外
好了,上面幾個問題都回答了,谷歌官方文件寫的非常的詳細。那麼,問題又來了
谷歌為什麼要設計生命週期中的這幾種狀態呢?
從谷歌的官方文件可以看出,onStart,是通過是否可見這種狀態來作為區分,onResume則是通過是否可以互動來區分,onPause設計的是與onResume相對應,onStop與onStart相對應,onCreate則是與onDestroy對應。
可以看出,谷歌在設計Activity的生命週期時,主要的依據是,是否可見以及是否可互動。那麼這兩種狀態對於Activity來說有哪些影響呢?
仔細想想,不難看出,在正常狀況下
,Activity的生命週期也是根據這兩個因素來運作的。失去焦點時,流程會走向onPause,獲取焦點,則會走向onResume。完全不可見時,會走向onStop,後面又可見時,會去調取onStart。正常狀況下
的狀態變換,都是圍繞著這兩個因素來流動的。
onCreate中做動畫失效、設定屬性失敗的原因?
這個問題,我想stackoverflow上有非常多的解決方案,但是從根本上去說,是因為onCreate的時候,元素還不可見,所以做不了動畫,一些控制元件無法去設定屬性。
onPause中處理時間過長,是否會引起問題呢?
如果時間過長,會引發ANR。如果時間大於限定值500ms,會出現 pause timeout 的警告。
最後的話
本文只是單純的從Activity生命週期的角度,來分析一些問題,並對一些現狀做了解釋。當然,我所表述的有可能會有問題,歡迎大家指正。
平時接觸到的書籍以及網上的資料,包括日常的開發中,這些問題的分析解答少之又少。這些問題也一直困擾著我,因此我才寫了這篇文章,就最常見的東西,換個角度去看問題。我所參考的資料簡單的不能再簡單了,就是谷歌文件,一些結論也是依據文件中的描述所推導的。
最後感謝大家的閱讀。