浅析Activity中View的生命周期方法回调

0 前言

Activity有生命周期,同样的,View从添加到界面到从界面中移除也有一个生命周期,在官方文档中介绍了自定义View需要重写的一些方法,可以认为这些方法就是View的生命周期方法。

View的生命周期方法.png

本文就介绍一下这些方法调用的顺序流程以及与Activity的生命周期结合之后的顺序流程。

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()方法中调用setContentView方法,Activity显示到界面时的View的回调

  1. 构造方法,这是肯定的,View也是一个Java类。
  2. onFinishInflate,这个一般是通过LayoutInflater进行填充的时候会走这个方法。如果我们是直接在代码中new出来的View进行添加,是不会走这个方法的。
  3. onAttachedToWindow,这个方法表明现在这个View已经跟它对应的Window已经绑定了
  4. onWindowVisibilitChanged(int visibility) ,这个值等于 View.VISIBLE,代表View所在的Window已经可见了。
  5. onMeasure,开始测量。我们发现,这个measure过程是在Window可见的情况下才会去调用了,仔细想想这个也不难理解,如果你都不准备显示,我何必去花精力测量你呢。这个测量过程可能会多次调用。
  6. onSizeChanged ,测量之后会回调这个方法。onSizeChanged,顾名思义就是当尺寸发生变化的时候会调用。一般是第一次测量之后调用,后面再测量,如果尺寸没变化就不会再去调用了。
  7. onLayout,测量时候就进行布局,这个时候如果是View的话一般不用去管,因为具体放在哪个位置是由父控件去控制的,如果是ViewGroup,就需要去确定子View的位置。
  8. onDraw,确定完位置和宽高,就可以进行绘制了。
  9. onWindowFocusChanged(boolean hasWindowFocus),为true这个说明View所绑定的Window开始获取焦点

当按back键退出当前Activity后,走下面几个方法

  1. onWindowVisibilitChanged(int visibility) ,这个值等于 View.GONE,此时Window已经不可见了
  2. onWindowFocusChanged(boolean hasWindowFocus),这个也变为false,说明已经没有焦点了。有一点比较奇怪,为什么是先不可见才是没有焦点的呢?
  3. onDetachedFromWindow, 当前View与它对应的Window解除绑定。

2 Activity和View的生命周期结合

当Activity和View的生命周期结合,我们会发现一些有意思的东西。


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: 

结合与Activity的启动过程可以看到

  1. Activity 调用onCreate方法,这个时候我们setContentView加载了带View的布局
  2. Activity 调用onWindowAttributesChanged 方法,而且这个方法连续调用多次
  3. View 调用构造方法
  4. View 调用onFinishInflate方法,说明这个时候View已经填充完毕,但是这个时候还没开始触发绘制过程
  5. Activity 调用onstart方法
  6. Activity 再次调用 onWindowAttributesChanged 方法,说明这个方法在onResume之前会多次调用
  7. Activity 调用onResume,我们一般认为当Activity调用onResume的时候,整个Activit已经可以和用户进行交互了,但事实上可能并不是这样,后面解释原因。
  8. Activity 调用onAttachedToWindow,说明跟Window进行了绑定。发现了吗,Activity在onResume之后才跟Window进行了绑定。
  9. View 调用onAttachedToWindow,View开始跟Window进行绑定,这个过程肯定是在Activity绑定之后才进行的。
  10. View 调用 onWindowVisibilityChanged(int visibility),参数变为 View.VISIABLE,说明Window已经可见了,这个时候我们发现一个问题就是其实onResume的时候似乎并不代表Activity中的View已经可见了。
  11. View 调用onMeasure,开始测量
  12. View 调用onSizeChanged,表示测量完成,尺寸发生了变化
  13. View 调用onLayout,开始摆放位置
  14. View 调用 onDraw,开始绘制
  15. Activity 调用onWindowFocusChanged(boolean hasFocus),此时为true,代表窗体已经获取了焦点
  16. View 调用 onWindowFocusChanged(boolean hasWindowFocus),此时为true,代表当前的控件获取了Window焦点,当调用这个方法后说明当前Activity中的View才是真正的可见了。

当退出当前的Activity的时候

  1. Activity 调用 onPause
  2. View 调用 onWindowVisibilityChanged(int visibility),参数变为 View.GONE,View中对应的Window隐藏
  3. Activity 调用onWindowFocusChanged(boolean hasFocus),此时为false,说明Actvity所在的Window已经失去焦点
  4. Activity 调用 onStop,此时Activity已经切换到后台
  5. Activity 调用 onDestory,此时Activity开始准备销毁,实际上调用onDestory并不代表Activity已经销毁了。
  6. View 调用 onDetachedFromWindow,此时View 与Window解除绑定
  7. Activity 调用 onDetathedFromWindow ,此时Activity 与Window 解除绑定

当View进行与Window解除绑定之后,View即将被销毁。我们可以在 View 的 onDetachedFromWindow 方法中可以做一些资源的释放,防止内存泄漏。

3 Activity的onWindowFocusChanged(boolean hasFocus)

从上面的分析我们可以知道,一个Activity启动后onCreate、onStart、onResume等过程后,Activity并不是真正可见的,只有当 onWindowFocusChanged 方法最后调用并且参数为true的时候Activity才是真正的可见,这个时候才可以和用户进行交互。

我们可以这 onWindowFocusChanged 可以做一些事情。比如, 获取布局中的控件的尺寸。

从 Activity 中的 onWindowFoucusChanged 方法介绍来看,当包含 View的 Window 获得或者失去焦点就会调用这个方法。而且要注意,它和View的焦点是有区别的。为了接收键盘事件,View和Window都必须获得焦点。而当一个显示在你的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对用户可见的最佳指示器。例如一个系统Window如键盘可能是处在前面。采用onWindowFocusChanged可以确定当前的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 总结

  1. 熟知 View 的生命周期方法,可加深我们对于View的理解,在开发中也可以根据这个生命周期方法的回调时机来写出更加合理高效的自定义View,例如在 View 的 onDetachedFromWindow 方法中释放资源。
  2. Activity还有其它的生命周期方法,比如 onSaveInstanceState(Bundle outState)onRestoreInstanceState(Bundle savedInstanceState) 等,限于篇幅和主题,本文并没有介绍这些方法,但是其实这些方法在 Activity 的生命周期方法中也是很重要的。
  3. 通常认为Activity的 onResume 方法调用之后,就可以与用户交互,通过本文的分析可知,这种说法并不准确。只有在 Activity 的 onWindowFocusChanged(boolean hasFocus) 调用,并且参数为true的时候,才是真正可以和用户交互的时机。
  4. Activity 和 View 的很多生命周期回调方法都牵涉到 Window,这个Window 是Android 系统中的一个抽象类,它有一个唯一的子类叫作 PhoneWindow, 它具体有什么作用,在以后的文章中会介绍到它。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容