问题场景:在项目中的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();
}