0 前言
Activity有生命周期,同樣的,View從添加到界面到從界面中移除也有一個生命周期,在官方文檔中介紹了自定義View需要重寫的一些方法,可以認為這些方法就是View的生命周期方法。

本文就介紹一下這些方法調(diào)用的順序流程以及與Activity的生命周期結(jié)合之后的順序流程。
1 View的生命周期
從Activity啟動到退出,這個View 的過程是這樣的。
D/ViewLifeTestView: ViewLifeTestView: construct
D/ViewLifeTestView: onFinishInflate:
D/ViewLifeTestView: onAttachedToWindow:
D/ViewLifeTestView: onWindowVisibilityChanged: visiable
D/ViewLifeTestView: onMeasure:
D/ViewLifeTestView: onMeasure:
D/ViewLifeTestView: onSizeChanged:
D/ViewLifeTestView: onLayout:
D/ViewLifeTestView: onDraw:
D/ViewLifeTestView: onWindowFocusChanged: true
D/ViewLifeTestView: onWindowVisibilityChanged: gone
D/ViewLifeTestView: onWindowFocusChanged: false
D/ViewLifeTestView: onDetachedFromWindow:

在Activity的onCreate()方法中調(diào)用setContentView方法,Activity顯示到界面時的View的回調(diào)
- 構(gòu)造方法,這是肯定的,View也是一個Java類。
- onFinishInflate,這個一般是通過LayoutInflater進行填充的時候會走這個方法。如果我們是直接在代碼中new出來的View進行添加,是不會走這個方法的。
- onAttachedToWindow,這個方法表明現(xiàn)在這個View已經(jīng)跟它對應(yīng)的Window已經(jīng)綁定了
- onWindowVisibilitChanged(int visibility) ,這個值等于 View.VISIBLE,代表View所在的Window已經(jīng)可見了。
- onMeasure,開始測量。我們發(fā)現(xiàn),這個measure過程是在Window可見的情況下才會去調(diào)用了,仔細想想這個也不難理解,如果你都不準(zhǔn)備顯示,我何必去花精力測量你呢。這個測量過程可能會多次調(diào)用。
- onSizeChanged ,測量之后會回調(diào)這個方法。onSizeChanged,顧名思義就是當(dāng)尺寸發(fā)生變化的時候會調(diào)用。一般是第一次測量之后調(diào)用,后面再測量,如果尺寸沒變化就不會再去調(diào)用了。
- onLayout,測量時候就進行布局,這個時候如果是View的話一般不用去管,因為具體放在哪個位置是由父控件去控制的,如果是ViewGroup,就需要去確定子View的位置。
- onDraw,確定完位置和寬高,就可以進行繪制了。
- onWindowFocusChanged(boolean hasWindowFocus),為true這個說明View所綁定的Window開始獲取焦點
當(dāng)按back鍵退出當(dāng)前Activity后,走下面幾個方法
- onWindowVisibilitChanged(int visibility) ,這個值等于 View.GONE,此時Window已經(jīng)不可見了
- onWindowFocusChanged(boolean hasWindowFocus),這個也變?yōu)閒alse,說明已經(jīng)沒有焦點了。有一點比較奇怪,為什么是先不可見才是沒有焦點的呢?
- onDetachedFromWindow, 當(dāng)前View與它對應(yīng)的Window解除綁定。
2 Activity和View的生命周期結(jié)合
當(dāng)Activity和View的生命周期結(jié)合,我們會發(fā)現(xiàn)一些有意思的東西。
D/ViewLifeTestActivity: onCreate:
D/ViewLifeTestActivity: onWindowAttributesChanged:
...
D/ViewLifeTestView: ViewLifeTestView: construct
D/ViewLifeTestView: onFinishInflate:
D/ViewLifeTestActivity: onStart:
D/ViewLifeTestActivity: onWindowAttributesChanged:
D/ViewLifeTestActivity: onResume:
D/ViewLifeTestActivity: onAttachedToWindow:
D/ViewLifeTestView: onAttachedToWindow:
D/ViewLifeTestView: onWindowVisibilityChanged: visiable
D/ViewLifeTestView: onMeasure:
D/ViewLifeTestView: onMeasure:
D/ViewLifeTestView: onSizeChanged:
D/ViewLifeTestView: onLayout:
D/ViewLifeTestView: onDraw:
D/ViewLifeTestActivity: onWindowFocusChanged hashFocus: true
D/ViewLifeTestView: onWindowFocusChanged: true
D/ViewLifeTestView: onTouchEvent:
D/ViewLifeTestActivity: onPause:
D/ViewLifeTestView: onWindowVisibilityChanged: gone
D/ViewLifeTestActivity: onWindowFocusChanged hashFocus: false
D/ViewLifeTestView: onWindowFocusChanged: false
D/ViewLifeTestActivity: onStop:
D/ViewLifeTestActivity: onDestroy:
D/ViewLifeTestView: onDetachedFromWindow:
D/ViewLifeTestActivity: onDetachedFromWindow:
結(jié)合與Activity的啟動過程可以看到
- Activity 調(diào)用onCreate方法,這個時候我們setContentView加載了帶View的布局
- Activity 調(diào)用onWindowAttributesChanged 方法,而且這個方法連續(xù)調(diào)用多次
-
View調(diào)用構(gòu)造方法 -
View調(diào)用onFinishInflate方法,說明這個時候View已經(jīng)填充完畢,但是這個時候還沒開始觸發(fā)繪制過程 - Activity 調(diào)用onstart方法
- Activity 再次調(diào)用 onWindowAttributesChanged 方法,說明這個方法在onResume之前會多次調(diào)用
- Activity 調(diào)用onResume,我們一般認為當(dāng)Activity調(diào)用onResume的時候,整個Activit已經(jīng)可以和用戶進行交互了,但事實上可能并不是這樣,后面解釋原因。
- Activity 調(diào)用onAttachedToWindow,說明跟Window進行了綁定。發(fā)現(xiàn)了嗎,Activity在onResume之后才跟Window進行了綁定。
-
View調(diào)用onAttachedToWindow,View開始跟Window進行綁定,這個過程肯定是在Activity綁定之后才進行的。 -
View調(diào)用 onWindowVisibilityChanged(int visibility),參數(shù)變?yōu)?View.VISIABLE,說明Window已經(jīng)可見了,這個時候我們發(fā)現(xiàn)一個問題就是其實onResume的時候似乎并不代表Activity中的View已經(jīng)可見了。 -
View調(diào)用onMeasure,開始測量 -
View調(diào)用onSizeChanged,表示測量完成,尺寸發(fā)生了變化 -
View調(diào)用onLayout,開始擺放位置 -
View調(diào)用 onDraw,開始繪制 - Activity 調(diào)用onWindowFocusChanged(boolean hasFocus),此時為true,代表窗體已經(jīng)獲取了焦點
-
View調(diào)用 onWindowFocusChanged(boolean hasWindowFocus),此時為true,代表當(dāng)前的控件獲取了Window焦點,當(dāng)調(diào)用這個方法后說明當(dāng)前Activity中的View才是真正的可見了。
當(dāng)退出當(dāng)前的Activity的時候
- Activity 調(diào)用 onPause
-
View調(diào)用 onWindowVisibilityChanged(int visibility),參數(shù)變?yōu)?View.GONE,View中對應(yīng)的Window隱藏 - Activity 調(diào)用onWindowFocusChanged(boolean hasFocus),此時為false,說明Actvity所在的Window已經(jīng)失去焦點
- Activity 調(diào)用 onStop,此時Activity已經(jīng)切換到后臺
- Activity 調(diào)用 onDestory,此時Activity開始準(zhǔn)備銷毀,實際上調(diào)用onDestory并不代表Activity已經(jīng)銷毀了。
-
View調(diào)用 onDetachedFromWindow,此時View 與Window解除綁定 - Activity 調(diào)用 onDetathedFromWindow ,此時Activity 與Window 解除綁定
當(dāng)View進行與Window解除綁定之后,View即將被銷毀。我們可以在 View 的 onDetachedFromWindow 方法中可以做一些資源的釋放,防止內(nèi)存泄漏。
3 Activity的onWindowFocusChanged(boolean hasFocus)
從上面的分析我們可以知道,一個Activity啟動后onCreate、onStart、onResume等過程后,Activity并不是真正可見的,只有當(dāng) onWindowFocusChanged 方法最后調(diào)用并且參數(shù)為true的時候Activity才是真正的可見,這個時候才可以和用戶進行交互。
我們可以這 onWindowFocusChanged 可以做一些事情。比如, 獲取布局中的控件的尺寸。
從 Activity 中的 onWindowFoucusChanged 方法介紹來看,當(dāng)包含 View的 Window 獲得或者失去焦點就會調(diào)用這個方法。而且要注意,它和View的焦點是有區(qū)別的。為了接收鍵盤事件,View和Window都必須獲得焦點。而當(dāng)一個顯示在你的Window上面的Window獲取輸入焦點的時候,你自己的Window失去了焦點,但是這個View本身的焦點不會改變。例如,彈出一個PopopWindow。
下面是 Activity 的 onWindowFocusChanged 方法介紹。
# Activity
/**
* Called when the window containing this view gains or loses focus. Note
* that this is separate from view focus: to receive key events, both
* your view and its window must have focus. If a window is displayed
* on top of yours that takes input focus, then your own window will lose
* focus but the view focus will remain unchanged.
*
* @param hasWindowFocus True if the window containing this view now has
* focus, false otherwise.
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (!hasWindowFocus) {
if (isPressed()) {
setPressed(false);
}
if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
imm.focusOut(this);
}
removeLongPressCallback();
removeTapCallback();
onFocusLost();
} else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
imm.focusIn(this);
}
refreshDrawableState();
}
在 Activity 的 onResume 方法中,可以看到有這么一段注釋。意思就是提醒大家 這個onResume 并不是提醒你這個Activty對用戶可見的最佳指示器。例如一個系統(tǒng)Window如鍵盤可能是處在前面。采用onWindowFocusChanged可以確定當(dāng)前的Activity對用戶可見并且是可交互的。
/**
* Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
* {@link #onPause}, for your activity to start interacting with the user.
* This is a good place to begin animations, open exclusive-access devices
* (such as the camera), etc.
*
* <p>Keep in mind that onResume is not the best indicator that your activity
* is visible to the user; a system window such as the keyguard may be in
* front. Use {@link #onWindowFocusChanged} to know for certain that your
* activity is visible to the user (for example, to resume a game).
*
* <p><em>Derived classes must call through to the super class's
* implementation of this method. If they do not, an exception will be
* thrown.</em></p>
4 總結(jié)
- 熟知 View 的生命周期方法,可加深我們對于View的理解,在開發(fā)中也可以根據(jù)這個生命周期方法的回調(diào)時機來寫出更加合理高效的自定義View,例如在 View 的
onDetachedFromWindow方法中釋放資源。 - Activity還有其它的生命周期方法,比如
onSaveInstanceState(Bundle outState),onRestoreInstanceState(Bundle savedInstanceState)等,限于篇幅和主題,本文并沒有介紹這些方法,但是其實這些方法在 Activity 的生命周期方法中也是很重要的。 - 通常認為Activity的
onResume方法調(diào)用之后,就可以與用戶交互,通過本文的分析可知,這種說法并不準(zhǔn)確。只有在 Activity 的onWindowFocusChanged(boolean hasFocus)調(diào)用,并且參數(shù)為true的時候,才是真正可以和用戶交互的時機。 - Activity 和 View 的很多生命周期回調(diào)方法都牽涉到 Window,這個Window 是Android 系統(tǒng)中的一個抽象類,它有一個唯一的子類叫作 PhoneWindow, 它具體有什么作用,在以后的文章中會介紹到它。