欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 中 Fragment 嵌套 Fragment使用存在的bug附完美解决方案

程序员文章站 2024-03-06 21:38:02
自从android3.0引入了fragment之后,使用activity去嵌套一些fragment的做法也变得更加流行,这确实是fragment带来的一些优点,比如说:fr...

自从android3.0引入了fragment之后,使用activity去嵌套一些fragment的做法也变得更加流行,这确实是fragment带来的一些优点,比如说:fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和ui,更重要的是fragment解决了activity间的切换不流畅,实现了一种轻量及的切换,但是在官方提供的android.support.v4包中,fragment还是或多或少的存在一些bug,今天就与大家分享一下这些bug和解决方法。

case 1:当使用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接收不到onactivityresult,如果在子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存在的原因之后,就可以完美的解决问题。希望对大家有所帮助