美文网首页Kotlin专题andnroid
探究jumpDrawablesToCurrentState问题发

探究jumpDrawablesToCurrentState问题发

作者: 旅旅人 | 来源:发表于2018-04-01 12:29 被阅读1128次

    你永远都追不上比你优秀的人,因为他们比你更努力~ 【今日份丧】

    最近日常的需求量激增,写代码写的石乐志。上周在实现一个ViewPager+Fragment的时候在Fragment里面的onCreateView写下了下面这行代码

      override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
          savedInstanceState: Bundle?): View? {
        mRootView = inflater?.inflate(R.layout.fragment_doemstic_city, container)
        initView()
        initIndexBar()
        initData()
        return mRootView
      }
    

    正常看也没觉得有毛病,但是一旦run起来,你会看到如下报错(移除部分信息):

    java.lang.StackOverflowError: stack size 8MB
      at android.view.ViewGroup.jumpDrawablesToCurrentState(ViewGroup.java:6958)
      at android.view.View.onAttachedToWindow(View.java:16862)
      at android.view.ViewGroup.onAttachedToWindow(ViewGroup.java:4837)
      at android.support.v4.view.ViewPager.onAttachedToWindow(ViewPager.java:1538)
      at android.view.View.dispatchAttachedToWindow(View.java:17377)
      at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3319)
      at android.view.ViewGroup.addViewInner(ViewGroup.java:4955)
      at android.view.ViewGroup.addViewInLayout(ViewGroup.java:4891)
      at android.view.ViewGroup.addViewInLayout(ViewGroup.java:4869)
      at android.support.v4.view.ViewPager.addView(ViewPager.java:1477)
      at android.view.ViewGroup.addView(ViewGroup.java:4686)
      at android.view.ViewGroup.addView(ViewGroup.java:4659)
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1427)
      at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1752)
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1821)
      at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:797)
      at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2595)
      at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2382)
      at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2337)
      at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2214)
      at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:649)
      at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:145)
      at android.support.v4.view.ViewPager.populate(ViewPager.java:1238)
      at android.support.v4.view.ViewPager.populate(ViewPager.java:1086)
      at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1616)
      at android.view.View.measure(View.java:21998)
      
    
    黑人问号

    怎么办??? 谷歌一下

    网上给出的说法和解法

    Inflate里面把ViewGroup传进去了,因为每一个View只能有一个父view即parentView。当container不为空时,比如此fragment所待在的activity的layout。而onCreateView中返回的view是给ViewPager使用的,所以就会出现这个view有两个parentView-即activity的layout和viewPager,所以会报出异常。解法如下:

    //错误示范
    mRootView = inflater?.inflate(R.layout.fragment_doemstic_city, container)
    //正确写法
     mRootView = inflater?.inflate(R.layout.fragment_doemstic_city, null)
     mRootView = inflater?.inflate(R.layout.fragment_doemstic_city, container,false)
    

    根据inflate的源码我们知道当传入的container不为null时,container会成为R.layout.XX布局的RootView
    但是难道不应该报错是The specified child already has a parent. You must call removeView() on the child’s parent first......为什么会是StackOverflowError

    不生气 不生气 Let's read the fucking source code

    首先根据错误信息可以知道这次事件发生的简单经过是:
    ViewPager(onMeasure -> populate -> addView)
    ViewGroup(addViewInLayout -> addViewInner)
    View(dispatchAttachedToWindow -> onAttachedToWindow -> jumpDrawablesToCurrentState)

    日常经验可知在onMeasure和populate时是得不到有用的信息,我们从addView的时候开始追吧!!

    ViewPager:addView

     @Override
        public void addView(View child, int index, ViewGroup.LayoutParams params) {
            if (!checkLayoutParams(params)) {
                params = generateLayoutParams(params);
            }
            final LayoutParams lp = (LayoutParams) params;
            // Any views added via inflation should be classed as part of the decor
            lp.isDecor |= isDecorView(child);
            if (mInLayout) {
                if (lp != null && lp.isDecor) {
                    throw new IllegalStateException("Cannot add pager decor view during layout");
                }
                lp.needsMeasure = true;
                addViewInLayout(child, index, params);
            } else {
                super.addView(child, index, params);
            }
    
            if (USE_CACHE) {
                if (child.getVisibility() != GONE) {
                    child.setDrawingCacheEnabled(mScrollingCacheEnabled);
                } else {
                    child.setDrawingCacheEnabled(false);
                }
            }
        }
    

    没有有用的信息,接着看addViewInLayout

    ViewPager:addViewInLayout

       protected boolean addViewInLayout(View child, int index, LayoutParams params) {
            return addViewInLayout(child, index, params, false);
        }
    
       protected boolean addViewInLayout(View child, int index, LayoutParams params,
                boolean preventRequestLayout) {
            if (child == null) {
                throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
            }
            //注意看这里,强行将child的parent置空,难怪没有出现The specified child already has a parent. You must call removeView() on the child’s parent first
            child.mParent = null;
            addViewInner(child, index, params, preventRequestLayout);
            child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
            return true;
        }
    

    有了一点点眉目

    ViewGroup:addViewInner

        if (mTransition != null) {
            // Don't prevent other add transitions from completing, but cancel remove
            // transitions to let them complete the process before we add to the container
            mTransition.cancel(LayoutTransition.DISAPPEARING);
        }
    
        //在ViewPager里面child.parent = null 所以这里才没有出发这个异常 为后面的StackOverflowError埋下了伏笔
        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }
    
        //省略无用信息
        AttachInfo ai = mAttachInfo;
        if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
            boolean lastKeepOn = ai.mKeepScreenOn;
            ai.mKeepScreenOn = false;
            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
            if (ai.mKeepScreenOn) {
                needGlobalAttributesUpdate(true);
            }
            ai.mKeepScreenOn = lastKeepOn;
        }
    
       //省略无用信息
    
    }
    

    好了到这里原因已经很明显了,因为ViewPager里面强行将childView的parentView置空,导致在ViewGroup里面没有检测出来,而实际情况确是因为错误的写法导致childView以及attach到了container上,而之后又被add到了ViewPager上,这种情况导致了整个布局树中存在循环引用,所以在最后会无限制的调用jumpDrawablesToCurrentState,递归无法结束....

    相关文章

      网友评论

        本文标题:探究jumpDrawablesToCurrentState问题发

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