美文网首页
子线程requestLayout导致界面显示异常-问题总结

子线程requestLayout导致界面显示异常-问题总结

作者: WLHere | 来源:发表于2020-09-17 20:43 被阅读0次

    正常的requestLayout流程

    1. 调用requestLayout()
    2. 设置标识PFLAG_FORCE_LAYOUT,标明请求了requestLayout
    3. 依次向上请求parent的requestLayout。如果parent已经请求过了,则不再向上请求
    4. 向上直到调用到ViewRootImpl的requestLayout方法。ViewRootImpl实现了ViewParent方法,而它是布局树顶点DecorView的parent
    5. ViewRootImpl执行requestLayout
      1. 检查线程
      2. 设置mLayoutRequested为true
      3. scheduleTraversals
    6. 待下一个vsync信号到来,执行performTraversals方法
    7. ViewRootImpl执行performTraversals。如果mLayoutRequested为true则调用mView.layout方法执行layout流程
    8. View.layout执行layout并重置PFLAG_FORCE_LAYOUT标志位。
    9. 界面正常显示

    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流程

    1. 子线程执行requestLayout,并在ViewRootImpl.reqeustLayout抛出异常。在checkThread()因为抛出异常没有设置mLayoutRequested = true,也没有scheduleTraversals,此次requestLayout终止。
    2. 因为View调用requestLayout时一路向上设置了PFLAG_FORCE_LAYOUT,因为没有发生layout所以此标志位一直是1。后续调用requetLayout因为标志位PFLAG_FORCE_LAYOUT是1,所以不会继续向上调用requestLayout。
    3. 没有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);
            }
        }
    
        /////////
    }
    

    相关文章

      网友评论

          本文标题:子线程requestLayout导致界面显示异常-问题总结

          本文链接:https://www.haomeiwen.com/subject/zqjryktx.html