正常的requestLayout流程
- 调用requestLayout()
- 设置标识PFLAG_FORCE_LAYOUT,标明请求了requestLayout
- 依次向上请求parent的requestLayout。如果parent已经请求过了,则不再向上请求
- 向上直到调用到ViewRootImpl的requestLayout方法。ViewRootImpl实现了ViewParent方法,而它是布局树顶点DecorView的parent
- ViewRootImpl执行requestLayout
- 检查线程
- 设置mLayoutRequested为true
- scheduleTraversals
- 待下一个vsync信号到来,执行performTraversals方法
- ViewRootImpl执行performTraversals。如果mLayoutRequested为true则调用mView.layout方法执行layout流程
- View.layout执行layout并重置PFLAG_FORCE_LAYOUT标志位。
- 界面正常显示
View.java
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
// 设置标识,标明请求了requestLayout
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
// 依次向上请求parent的requestLayout。如果parent已经请求过了,则不再向上请求
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
// 是否请求了requestLayout
public boolean isLayoutRequested() {
return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}
ViewRootImpl.java
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 如果不是主线程,这里会抛出异常
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
// mThread在ViewRootImpl的构造函数赋值mThread = Thread.currentThread()。而ViewRootImpl是在主线程创建的,所以mThread指向主线程
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
异常的requestLayout流程
- 子线程执行requestLayout,并在ViewRootImpl.reqeustLayout抛出异常。在checkThread()因为抛出异常没有设置mLayoutRequested = true,也没有scheduleTraversals,此次requestLayout终止。
- 因为View调用requestLayout时一路向上设置了PFLAG_FORCE_LAYOUT,因为没有发生layout所以此标志位一直是1。后续调用requetLayout因为标志位PFLAG_FORCE_LAYOUT是1,所以不会继续向上调用requestLayout。
- 没有requestLayout无法触发layout,后续add的view都无法显示出来,页面显示异常了
为什么切到后台后再切回前台,就恢复正常显示了?
切到后台再切回前台,触发scheduleTraversals。在performTraversals方法里边,因为可见性发生了改变,会设置layoutRequested为true,触发mView.layout,完成layout流程,也会把PFLAG_FORCE_LAYOUT置为0,后续requestLayout也就正常了。
ViewRootImpl.W
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
/////////
@Override
public void dispatchAppVisibility(boolean visible) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchAppVisibility(visible);
}
}
/////////
}
网友评论