作者:积木zz
Activity从创建到我们看到界面,发生了哪些事
- 首先是通过
setContentView
加载布局,这其中创建了一个DecorView
,然后根据然后根据activity设置的主题(theme)或者特征(Feature)加载不同的根布局文件,最后再通过inflate方法加载layoutResID
资源文件,其实就是解析了xml文件,根据节点生成了View对象。流程图:

- 其次就是进行view绘制到界面上,这个过程发生在
handleResumeActivity
方法中,也就是触发onResume
的方法。在这里会创建一个ViewRootImpl
对象,作为DecorView
的parent然后对DecorView
进行测量布局和绘制三大流程。流程图:

Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?
-
PhoneWindow
是Window 的唯一子类,每个Activity都会创建一个PhoneWindow对象,你可以理解它为一个窗口,但不是真正的可视窗口,而是一个管理类,是Activity和整个View系统交互的接口,是Activity和View交互系统的中间层。 -
DecorView
是PhoneWindow的一个内部类,是整个View层级的最顶层,一般包括标题栏和内容栏两部分,会根据不同的主题特性调整不同的布局。它是在setContentView方法中被创建,具体点来说是在PhoneWindow的installDecor方法中被创建。 -
ViewRootImp
l是DecorView的parent,用来控制View的各种事件,在handleResumeActivity
方法中被创建。
requestLayout和invalidate
-
requestLayout
方法是用来触发绘制流程,他会会一层层调用 parent 的requestLayout
,一直到最上层也就是ViewRootImpl的requestLayout,这里也就是判断线程的地方了,最后会执行到performMeasure -> performLayout -> performDraw
三个绘制流程,也就是测量——布局——绘制。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();//执行绘制流程
}
}
其中performMeasure
方法会执行到View的measure方法,用来测量大小。performLayout
方法会执行到view的layout方法,用来计算位置。performDraw
方法需要注意下,他会执行到view的draw方法,但是并不一定会进行绘制,只有只有 flag 被设置为 PFLAG_DIRTY_OPAQUE
才会进行绘制。
- invalidate方法也是用来触发绘制流程,主要表现就是会调用draw()方法。虽然他也会走到
scheduleTraversals
方法,也就是会走到三大流程,但是View会通过mPrivateFlags
来判断是否进行onMeasure和onLayout操作。而在用invalidate方法时,更新了mPrivateFlags
,所以不会进行measure和layout。同时他也会设置Flag为PFLAG_DIRTY_OPAQUE,所以肯定会执行onDraw方法。
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
//...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();//执行绘制流程
}
}
最后看一下scheduleTraversals
方法中三大绘制流程逻辑,是不是我们之前说的那样,FORCE_LAYOUT标志才会onMeasure和onLayout,PFLAG_DIRTY_OPAQUE
标志才会onDraw:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
// 只有mPrivateFlags为PFLAG_FORCE_LAYOUT的时候才会进行onMeasure方法
if (forceLayout || needsLayout) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 设置 LAYOUT_REQUIRED flag
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
public void layout(int l, int t, int r, int b) {
...
//判断标记位为PFLAG_LAYOUT_REQUIRED的时候才进行onLayout方法
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
}
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
// flag 是 PFLAG_DIRTY_OPAQUE 则需要绘制
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
if (!dirtyOpaque) {
drawBackground(canvas);
}
if (!dirtyOpaque) onDraw(canvas);
// 绘制 Child
dispatchDraw(canvas);
// foreground 不管 dirtyOpaque 标志,每次都会绘制
onDrawForeground(canvas);
}
参考文章中有一段总结挺好的:
虽然两者都是用来触发绘制流程,但是在measure和layout过程中,只会对 flag 设置为 FORCE_LAYOUT 的情况进行重新测量和布局,而draw方法中只会重绘flag为 dirty 的区域。
requestLayout 是用来设置FORCE_LAYOUT标志,invalidate 用来设置 dirty 标志。所以 requestLayout 只会触发 measure 和 layout,invalidate 只会触发 draw。
喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗😜😜😜~
点击【GitHub】还有彩蛋哦!!!

网友评论