天天看点

Exception:Duplicate id 0x7f0b00d6, tag null, or parent id 0xffffffff with another fragment

问题场景:在项目中的MineFragment中使用第三方的MsgListFragment,在销毁MineFragment的时候,抛出异常,如下:

Caused by: java.lang.IllegalArgumentException: Binary XML file line #50: Duplicate id 0x7f0b00d6, tag null, or parent id 0xffffffff with another fragment for...

堆栈信息:

08-18 22:45:47.290: E/AndroidRuntime(2113): Caused by: java.lang.IllegalArgumentException: Binary XML file line #50: Duplicate id 0x7f0b00d6, tag null, or parent id 0xffffffff with another fragment for MsgListFragment

08-18 22:45:47.290: E/AndroidRuntime(2113):     at android.support.v4.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2293)

08-18 22:45:47.290: E/AndroidRuntime(2113):     at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)

08-18 22:45:47.290: E/AndroidRuntime(2113):     at android.view.LayoutInflater$FactoryMerger.onCreateView(LayoutInflater.java:168)

08-18 22:45:47.290: E/AndroidRuntime(2113):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:675)

08-18 22:45:47.290: E/AndroidRuntime(2113):     ... 30 more

大致意思,在MineFragment对应xml布局中,某个id被MsgListFragment重复使用

于是,查看下FragmentManagerImpl,如下:

public View onCreateView(View parent, String name, Context context, AttributeSet attrs)
  {
    if (!"fragment".equals(name)) {
      return null;
    }

    String fname = attrs.getAttributeValue(null, "class");
    TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment);//<span style="color:#FF0000;">位置1</span>
    if (fname == null) {
      fname = a.getString(0);
    }
    int id = a.getResourceId(1, -1);//<span style="color:#FF0000;">位置2</span>
    String tag = a.getString(2);
    a.recycle();

    if (!Fragment.isSupportFragmentClass(this.mHost.getContext(), fname))
    {
      return null;
    }

    int containerId = parent != null ? parent.getId() : 0;//<span style="color:#FF0000;">位置4</span>
    if ((containerId == -1) && (id == -1) && (tag == null)) {
      throw new IllegalArgumentException(new StringBuilder().append(attrs.getPositionDescription()).append(": Must specify unique android:id, android:tag, or have a parent with an id for ").append(fname).toString());
    }

    Fragment fragment = id != -1 ? findFragmentById(id) : null;//<span style="color:#FF0000;">关键,位置5</span>
    if ((fragment == null) && (tag != null)) {
      fragment = findFragmentByTag(tag);
    }
    if ((fragment == null) && (containerId != -1)) {
      fragment = findFragmentById(containerId);
    }

    if (DEBUG) Log.v("FragmentManager", new StringBuilder().append("onCreateView: id=0x").append(Integer.toHexString(id)).append(" fname=").append(fname).append(" existing=").append(fragment).toString());

    if (fragment == null) {//<span style="color:#FF0000;">位置6</span>
      fragment = Fragment.instantiate(context, fname);
      fragment.mFromLayout = true;
      fragment.mFragmentId = (id != 0 ? id : containerId);
      fragment.mContainerId = containerId;
      fragment.mTag = tag;
      fragment.mInLayout = true;
      fragment.mFragmentManager = this;
      fragment.mHost = this.mHost;
      fragment.onInflate(this.mHost.getContext(), attrs, fragment.mSavedFragmentState);
      addFragment(fragment, true);
    } else {//<span style="color:#FF0000;">位置7</span>
      if (fragment.mInLayout)
      {//<span style="color:#FF0000;">异常出现</span>
        throw new IllegalArgumentException(new StringBuilder().append(attrs.getPositionDescription()).append(": Duplicate id 0x").append(Integer.toHexString(id)).append(", tag ").append(tag).append(", or parent id 0x").append(Integer.toHexString(containerId)).append(" with another fragment for ").append(fname).toString());
      }

      fragment.mInLayout = true;
      fragment.mHost = this.mHost;

      if (!fragment.mRetaining) {
        fragment.onInflate(this.mHost.getContext(), attrs, fragment.mSavedFragmentState);
      }

    }

    if ((this.mCurState < 1) && (fragment.mFromLayout))
      moveToState(fragment, 1, 0, 0, false);
    else {
      moveToState(fragment);
    }

    if (fragment.mView == null) {
      throw new IllegalStateException(new StringBuilder().append("Fragment ").append(fname).append(" did not create a view.").toString());
    }

    if (id != 0) {
      fragment.mView.setId(id);
    }
    if (fragment.mView.getTag() == null) {
      fragment.mView.setTag(tag);
    }
    return fragment.mView;
}
           

位置1:初始化内置属性等,得到TypeArray

位置2:获取资源id,tag,同时获得 id > -1

位置4:判断containerId

位置5:初步断定,执行findFragmentById方法后,fragment不为null

位置6、7:执行位置7,抛出异常

public Fragment findFragmentById(int id) {
    if (this.mAdded != null)
    {
      for (int i = this.mAdded.size() - 1; i >= 0; i--) {
        Fragment f = (Fragment)this.mAdded.get(i);
        if ((f != null) && (f.mFragmentId == id)) {//<span style="color:#FF0000;">位置8</span>
          return f;
        }
      }
    }
    if (this.mActive != null)
    {
      for (int i = this.mActive.size() - 1; i >= 0; i--) {
        Fragment f = (Fragment)this.mActive.get(i);
        if ((f != null) && (f.mFragmentId == id)) {
          return f;
        }
      }
    }
    return null;
}
           

位置8:遍历mAdded,判断id是否与各个frament对应mFragmentId相等,相等,即找到对应fragment

接下来,看下mAdded是何方神圣,如下:

public void addFragment(Fragment fragment, boolean moveToStateNow) {
    if (this.mAdded == null) {
      this.mAdded = new ArrayList();//<span style="color:#FF0000;">位置9</span>
    }
    if (DEBUG) Log.v("FragmentManager", new StringBuilder().append("add: ").append(fragment).toString());
    makeActive(fragment);
    if (!fragment.mDetached) {
      if (this.mAdded.contains(fragment)) {
        throw new IllegalStateException(new StringBuilder().append("Fragment already added: ").append(fragment).toString());
      }
      this.mAdded.add(fragment);
      fragment.mAdded = true;
      fragment.mRemoving = false;
      if ((fragment.mHasMenu) && (fragment.mMenuVisible)) {
        this.mNeedMenuInvalidate = true;
      }
      if (moveToStateNow)
        moveToState(fragment);
    }
  }
           

位置9:可知,mAdded是个容器,专门存放创建成功的Fragment,也就是说,当Fragment创建后,addFragment方法被调用

解决方案:

@Override
	public void onDestroyView() {
		MsgListFragment  fragment = (MsgListFragment) getChildFragmentManager().findFragmentById(R.id.msg_list);
		if(fragment != null){
			getFragmentManager().beginTransaction().remove(fragment).commit();
		}
		super.onDestroyView();
	}