@wangwangheng
2014-11-23T10:50:31.000000Z
字数 3000
阅读 3798
Fragment
转载自推酷文章 , 并做了格式化处理
自从Android3.0引入了Fragment
之后,使用Activity
去嵌套一些Fragment
的做法也变得更加流行,这确实是Fragment
带来的一些优点,比如说:Fragment
可以使你能够将activity
分离成多个可重用的组件,每个都有它自己的生命周期和UI,更重要的是Fragment
解决了Activity
间的切换不流畅,实现了一种轻量及的切换,但是在官方提供的android.support.v4
包中,Fragment
还是或多或少的存在一些BUG,今天就与大家分享一下这些BUG和解决方法。
当使用Fragment
去嵌套另外一些子Fragment
的时候,我们需要去管理子Fragment
,这时候需要调用ChildFragmentManager
去管理这些子Fragment
,由此可能产生的Exception
主要是:
java.lang.IllegalStateException: No activity
首先我们来分析一下Exception
出现的原因:
通过DEBUG发现,当第一次从一个Activity
启动Fragment
,然后再去启动子Fragment的时候,存在指向Activity
的变量,但当退出这些Fragment
之后回到Activity
,然后再进入Fragment
的时候,这个变量变成null,这就很容易明了为什么抛出的异常是No activity
这个Exception是由什么原因造成的呢?如果想知道造成异常的原因,那就必须去看Fragment
的相关代码,发现Fragment
在detached
之后都会被reset掉,但是它并没有对ChildFragmentManager
做reset,所以会造成ChildFragmentManager
的状态错误。
找到异常出现的原因后就可以很容易的去解决问题了,我们需要在Fragment
被detached
的时候去重置ChildFragmentManager
,即:
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class
.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Case 2:当我们从一个Activity
启动了一个Fragment
,然后在这个Fragment
中又去实例化了一些子Fragment
,在子Fragment
中去有返回的启动了另外一个Activity
,即通过startActivityForResult
方式去启动,这时候造成的现象会是,子Fragment接收不到OnActivityResul
t,如果在子Fragment中是以getActivity.startActivityForResult
方式启动,那么只有Activity
会接收到OnActivityResult
,如果是以getParentFragment.startActivityForResult
方式启动,那么只有父Fragment
能接收(此时Activity也能接收),但无论如何子Fragment
接收不到OnActivityResult
。
这是一个非常奇怪的现象,按理说,应该是让子Fragment
接收到OnActivityResult
才对,究竟是什么造成的呢?这是由于某位写代码的员工抱怨没发奖金,稍稍偷懒了,少写了一部分代码,没有考虑到Fragment
再去嵌套Fragment
的情况。
我们来看看FragmentActivity中
的代码:
protected void onActivityResult(int requestCode, int resultCode, Intent data){
this.mFragments.noteStateNotSaved();
int index = requestCode >> 16;
if (index != 0) {
index--;
if ((this.mFragments.mActive == null)
|| (index < 0)
|| (index >= this.mFragments.mActive.size())) {
Log.w("FragmentActivity", "Activity result fragment index out of range: 0x"
+ Integer.toHexString(requestCode));
return;
}
Fragment frag = (Fragment)this.mFragments.mActive.get(index);
if (frag == null) {
Log.w("FragmentActivity", "Activity result no fragment exists for index: 0x"
+ Integer.toHexString(requestCode));
}else {
frag.onActivityResult(requestCode & 0xFFFF, resultCode, data);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
很显然,设计者把Fragment
的下标+1左移16位来标记这个request
是不是Fragment
的,拿到result
再解码出下标,直接取对应的Fragment
,这样并没有去考虑对Fragment
嵌套Fragment
做一个Map映射,所以出现了这种BUG。
但是如果我们需要在OnActivityResult
的时候处理一些事情的话,我们可以通过在子Fragment
中以getParentFragment.startActivityForResult
的方式来启动,然后在父Fragment
中去接收数据,我们需要在子Fragment
中提供一个方法,如:getResultData(Object obj)
,通过父Fragment
中的子Fragment
实例去调用这个方法,把相应的数据传过去,然后去更新子Fragment
。
以上是在使用Fragment
去嵌套Fragment
的时候可能会遇到的BUG,了解了BUG存在的原因之后,就可以完美的解决问题。