淺析Activity中View的生命周期方法回調(diào)

0 前言

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

View的生命周期方法.png

本文就介紹一下這些方法調(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: 

Paste_Image.png

在Activity的onCreate()方法中調(diào)用setContentView方法,Activity顯示到界面時的View的回調(diào)

  1. 構(gòu)造方法,這是肯定的,View也是一個Java類。
  2. onFinishInflate,這個一般是通過LayoutInflater進行填充的時候會走這個方法。如果我們是直接在代碼中new出來的View進行添加,是不會走這個方法的。
  3. onAttachedToWindow,這個方法表明現(xiàn)在這個View已經(jīng)跟它對應(yīng)的Window已經(jīng)綁定了
  4. onWindowVisibilitChanged(int visibility) ,這個值等于 View.VISIBLE,代表View所在的Window已經(jīng)可見了。
  5. onMeasure,開始測量。我們發(fā)現(xiàn),這個measure過程是在Window可見的情況下才會去調(diào)用了,仔細想想這個也不難理解,如果你都不準(zhǔn)備顯示,我何必去花精力測量你呢。這個測量過程可能會多次調(diào)用。
  6. onSizeChanged ,測量之后會回調(diào)這個方法。onSizeChanged,顧名思義就是當(dāng)尺寸發(fā)生變化的時候會調(diào)用。一般是第一次測量之后調(diào)用,后面再測量,如果尺寸沒變化就不會再去調(diào)用了。
  7. onLayout,測量時候就進行布局,這個時候如果是View的話一般不用去管,因為具體放在哪個位置是由父控件去控制的,如果是ViewGroup,就需要去確定子View的位置。
  8. onDraw,確定完位置和寬高,就可以進行繪制了。
  9. onWindowFocusChanged(boolean hasWindowFocus),為true這個說明View所綁定的Window開始獲取焦點

當(dāng)按back鍵退出當(dāng)前Activity后,走下面幾個方法

  1. onWindowVisibilitChanged(int visibility) ,這個值等于 View.GONE,此時Window已經(jīng)不可見了
  2. onWindowFocusChanged(boolean hasWindowFocus),這個也變?yōu)閒alse,說明已經(jīng)沒有焦點了。有一點比較奇怪,為什么是先不可見才是沒有焦點的呢?
  3. 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的啟動過程可以看到

  1. Activity 調(diào)用onCreate方法,這個時候我們setContentView加載了帶View的布局
  2. Activity 調(diào)用onWindowAttributesChanged 方法,而且這個方法連續(xù)調(diào)用多次
  3. View 調(diào)用構(gòu)造方法
  4. View 調(diào)用onFinishInflate方法,說明這個時候View已經(jīng)填充完畢,但是這個時候還沒開始觸發(fā)繪制過程
  5. Activity 調(diào)用onstart方法
  6. Activity 再次調(diào)用 onWindowAttributesChanged 方法,說明這個方法在onResume之前會多次調(diào)用
  7. Activity 調(diào)用onResume,我們一般認為當(dāng)Activity調(diào)用onResume的時候,整個Activit已經(jīng)可以和用戶進行交互了,但事實上可能并不是這樣,后面解釋原因。
  8. Activity 調(diào)用onAttachedToWindow,說明跟Window進行了綁定。發(fā)現(xiàn)了嗎,Activity在onResume之后才跟Window進行了綁定。
  9. View 調(diào)用onAttachedToWindow,View開始跟Window進行綁定,這個過程肯定是在Activity綁定之后才進行的。
  10. View 調(diào)用 onWindowVisibilityChanged(int visibility),參數(shù)變?yōu)?View.VISIABLE,說明Window已經(jīng)可見了,這個時候我們發(fā)現(xiàn)一個問題就是其實onResume的時候似乎并不代表Activity中的View已經(jīng)可見了。
  11. View 調(diào)用onMeasure,開始測量
  12. View 調(diào)用onSizeChanged,表示測量完成,尺寸發(fā)生了變化
  13. View 調(diào)用onLayout,開始擺放位置
  14. View 調(diào)用 onDraw,開始繪制
  15. Activity 調(diào)用onWindowFocusChanged(boolean hasFocus),此時為true,代表窗體已經(jīng)獲取了焦點
  16. View 調(diào)用 onWindowFocusChanged(boolean hasWindowFocus),此時為true,代表當(dāng)前的控件獲取了Window焦點,當(dāng)調(diào)用這個方法后說明當(dāng)前Activity中的View才是真正的可見了。

當(dāng)退出當(dāng)前的Activity的時候

  1. Activity 調(diào)用 onPause
  2. View 調(diào)用 onWindowVisibilityChanged(int visibility),參數(shù)變?yōu)?View.GONE,View中對應(yīng)的Window隱藏
  3. Activity 調(diào)用onWindowFocusChanged(boolean hasFocus),此時為false,說明Actvity所在的Window已經(jīng)失去焦點
  4. Activity 調(diào)用 onStop,此時Activity已經(jīng)切換到后臺
  5. Activity 調(diào)用 onDestory,此時Activity開始準(zhǔn)備銷毀,實際上調(diào)用onDestory并不代表Activity已經(jīng)銷毀了。
  6. View 調(diào)用 onDetachedFromWindow,此時View 與Window解除綁定
  7. 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é)

  1. 熟知 View 的生命周期方法,可加深我們對于View的理解,在開發(fā)中也可以根據(jù)這個生命周期方法的回調(diào)時機來寫出更加合理高效的自定義View,例如在 View 的 onDetachedFromWindow 方法中釋放資源。
  2. Activity還有其它的生命周期方法,比如 onSaveInstanceState(Bundle outState) , onRestoreInstanceState(Bundle savedInstanceState) 等,限于篇幅和主題,本文并沒有介紹這些方法,但是其實這些方法在 Activity 的生命周期方法中也是很重要的。
  3. 通常認為Activity的 onResume 方法調(diào)用之后,就可以與用戶交互,通過本文的分析可知,這種說法并不準(zhǔn)確。只有在 Activity 的 onWindowFocusChanged(boolean hasFocus) 調(diào)用,并且參數(shù)為true的時候,才是真正可以和用戶交互的時機。
  4. Activity 和 View 的很多生命周期回調(diào)方法都牽涉到 Window,這個Window 是Android 系統(tǒng)中的一個抽象類,它有一個唯一的子類叫作 PhoneWindow, 它具體有什么作用,在以后的文章中會介紹到它。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容