Fragment系列文章:
1、Fragment全解析系列(一):那些年踩过的坑
2、Fragment全解析系列(二):正确的使用姿势
3、Fragment之我的解决方案:Fragmentation
本篇主要介绍一些最常见的Fragment的坑以及官方Fragment库的那些自身的BUG,并给出解决方案;这些BUG在你深度使用时会遇到,比如Fragment嵌套时或者单Activity+多Fragment架构时遇到的坑。
Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。
简陋的目录
1、getActivity()空指针
2、异常:Can not perform this action after onSaveInstanceState
3、Fragment重叠异常-----正确使用hide、show的姿势
4、Fragment嵌套的那些坑
5、未必靠谱的出栈方法remove()
6、多个Fragment同时出栈的深坑BUG
7、深坑 Fragment转场动画
开始之前
最新版知乎,单Activity多Fragment的架构,响应可以说非常“丝滑”,非要说缺点的话,就是没有转场动画,并且转场会有类似闪屏现象。我猜测可能和Fragment转场动画的一些BUG有关。(这系列的最后一篇文章我会给出我的解决方案,可以自定义转场动画,并能在各种特殊情况下正常运行。)
但是!Fragment相比较Activity要难用很多,在多Fragment以及嵌套Fragment的情况下更是如此。
更重要的是Fragment的坑真的太多了,看Square公司的这篇文章吧,Square:从今天开始抛弃Fragment吧!
当然,不能说不再用Fragment,Fragment的这些坑都是有解决办法的,官方也在逐步修复一些BUG。
下面罗列一些,有常见的,也有极度隐蔽的一些坑,也是我在用单Activity多Fragment时遇到的坑,可能有更多坑可以挖掘...
在这之前为了方便后面文章的介绍,先规定一个“术语”,安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)
在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate
方法调用后紧接着恢复(从onAttach
生命周期开始)。
getActivity()空指针
可能你遇到过getActivity()返回null,或者平时运行完好的代码,在“内存重启”之后,调用getActivity()的地方却返回null,报了空指针异常。
大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()
了宿主Activity。
比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。
解决办法:
更"安全"的方法:(对于Fragment已经onDetach这种情况,我们应该避免在这之后再去调用宿主Activity对象,比如取消这些异步任务,但我们的团队可能会有粗心大意的情况,所以下面给出的这个方案会保证安全)
在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)
里赋值,使用mActivity代替getActivity()
,保证Fragment即使在onDetach
后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些),即:
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
/**
* 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}
异常:Can not perform this action after onSaveInstanceState
有很多小伙伴遇到这个异常,这个异常产生的原因是:
在你离开当前Activity等情况下,系统会调用onSaveInstanceState()
帮你保存当前Activity的状态、数据等,直到再回到该Activity之前(onResume()
之前),你执行Fragment事务,就会抛出该异常!(一般是其他Activity的回调让当前页面执行事务的情况,会引发该问题)
解决方法:
- 1、该事务使用
commitAllowingStateLoss()
方法提交,但是有可能导致该次提交无效!(宿主Activity被强杀时)
对于
popBackStack()
没有对应的popBackStackAllowingStateLoss()
方法,所以可以在下次可见时提交事务,参考2
- 2、利用
onActivityForResult()
/onNewIntent()
,可以做到事务的完整性,不会丢失事务
一个简单的示例代码 :
// ReceiverActivity 或 其子Fragment:
void start(){
startActivityForResult(new Intent(this, SenderActivity.class), 100);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100 && resultCode == 100) {
// 执行Fragment事务
}
}
// SenderActivity 或 其子Fragment:
void do() { // 操作ReceiverActivity(或其子Fragment)执行事务
setResult(100);
finish();
}
Fragment重叠异常-----正确使用hide、show的姿势
在类onCreate()
的方法加载Fragment,并且没有判断saveInstanceState==null
或if(findFragmentByTag(mFragmentTag) == null)
,导致重复加载了同一个Fragment导致重叠。(PS:replace
情况下,如果没有加入回退栈,则不判断也不会造成重叠,但建议还是统一判断下)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 在页面重启时,Fragment会被保存恢复,而此时再加载Fragment会重复加载,导致重叠 ;
if(saveInstanceState == null){
// 或者 if(findFragmentByTag(mFragmentTag) == null)
// 正常情况下去 加载根Fragment
}
}
详细原因:从源码角度分析,为什么会发生Fragment重叠?
如果你add()
了几个Fragment,使用show()、hide()
方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。
原因是FragmentManager帮我们管理Fragment,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragment;
但是因为官方没有保存Fragment的mHidden属性,默认为false,即show状态,所以所有Fragment都是以show的形式恢复,我们看到了界面重叠。
(如果是replace
,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用replace
不会造成重叠现象)
v4-24.0.0+ 开始,官方修复了上述 没有保存mHidden的问题,所以如果你在使用24.0.0+的v4包,下面分析的2个解决方案可以自行跳过...
这里给出2个解决方案:
1、是大家比较熟悉的 findFragmentByTag
:
即在add()
或者replace()
时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag
找到对应的Fragment,并hide()
需要隐藏的fragment。
下面是个标准恢复写法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
如果你想恢复到用户离开时的那个Fragment的界面,你还需要在onSaveInstanceState(Bundle outState)
里保存离开时的那个可见的tag或下标,在onCreate
“内存重启”代码块中,取出tag/下标,进行恢复。
** 2、我的解决方案,9行代码解决所有情况的Fragment重叠:传送门**
Fragment嵌套的那些坑
其实一些小伙伴遇到的很多嵌套的坑,大部分都是由于对嵌套的栈视图产生混乱,只要理清栈视图关系,做好恢复相关工作以及正确选择是使用getFragmentManager()
还是getChildFragmentManager()
就可以避免这些问题。
这部分内容是我们感觉Fragment非常难用的一个点,我会在下一篇中,详细介绍使用Fragment嵌套的一些技巧,以及如何清晰分析各个层级的栈视图。
附:startActivityForResult接收返回问题
在support 23.2.0以下的支持库中,对于在嵌套子Fragment的startActivityForResult ()
,会发现无论如何都不能在onActivityResult()
中接收到返回值,只有最顶层的父Fragment才能接收到,这是一个support v4库的一个BUG,不过在前两天发布的support 23.2.0库中,已经修复了该问题,嵌套的子Fragment也能正常接收到返回数据了!
未必靠谱的出栈方法remove()
如果你想让某一个Fragment出栈,使用remove()
在加入回退栈时并不靠谱。
如果你在add的同时将Fragment加入回退栈:addToBackStack(name)的情况下,它并不能真正将Fragment从栈内移除,如果你在2秒后(确保Fragment事务已经完成)打印getSupportFragmentManager().getFragments()
,会发现该Fragment依然存在,并且依然可以返回到被remove的Fragment,而且是空白页面。
如果你没有将Fragment加入回退栈,remove方法可以正常出栈。
如果你加入了回退栈,popBackStack()
系列方法才能真正出栈,这也就引入下一个深坑,popBackStack(String tag,int flags)
等系列方法的BUG。
多个Fragment同时出栈的深坑BUG
6月17日更新: 在support-25.4.0版本,google意识到下面的问题,并修复了。 如果你使用25.4.0及以上版本,下面的方法不要再使用,google移除了mAvailIndices属性
在Fragment库中如下4个方法是可能产生BUG的:
1、popBackStack(String tag,int flags)
2、popBackStack(int id,int flags)
3、popBackStackImmediate(String tag,int flags)
4、popBackStackImmediate(int id,int flags)
上面4个方法作用是,出栈到tag/id的fragment,即一次多个Fragment被出栈。
1、FragmentManager栈中管理fragment下标位置的数组ArrayList<Integer> mAvailIndeices的BUG
下面的方法FragmentManagerImpl类方法,产生BUG的罪魁祸首是管理Fragment栈下标的mAvailIndeices
属性:
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
}
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
上面代码最终导致了栈内顺序不正确的问题,如下图:
上面的这个情况,会一次异常,一次正常。带来的问题就是“内存重启”后,各种异常甚至Crash。
发现这BUG的时候,我一脸懵比,幸好,stackoverflow上有大神给出了解决方案!hack FragmentManagerImpl
的mAvailIndices
,对其进行一次Collections.reverseOrder()
降序排序,保证栈内Fragment的index的正确。
public class FragmentTransactionBugFixHack {
public static void reorderIndices(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return;
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
if (fragmentManagerImpl.mAvailIndices != null && fragmentManagerImpl.mAvailIndices.size() > 1) {
Collections.sort(fragmentManagerImpl.mAvailIndices, Collections.reverseOrder());
}
}
}
使用方法就是通过popBackStackImmediate(tag/id)
多个Fragment后,调用
hanler.post(new Runnable(){
@Override
public void run() {
FragmentTransactionBugFixHack.reorderIndices(fragmentManager));
}
});
2、popBackStack的坑
popBackStack
和popBackStackImmediate
的区别在于前者是加入到主线队列的末尾,等其它任务完成后才开始出栈,后者是队列内的任务立即执行,再将出栈任务放到队列尾(可以理解为立即出栈)。
如果你popBackStack
多个Fragment后,紧接着beginTransaction()
add新的一个Fragment,接着发生了“内存重启”后,你再执行popBackStack()
,app就会Crash,解决方案是postDelay出栈动画时间再执行其它事务,但是根据我的观察不是很稳定。
我的建议是:如果你想出栈多个Fragment,你应尽量使用popBackStackImmediate(tag/id)
,而不是popBackStack(tag/id)
,如果你想在出栈后,立刻beginTransaction()
开始一项事务,你应该把事务的代码post/postDelay到主线程的消息队列里,下一篇有详细描述。
深坑 Fragment转场动画(仅分析v4包下的Fragment)
如果你的Fragment没有转场动画,或者使用setCustomAnimations(enter, exit)
的话,那么上面的那些坑解决后,你可以愉快的玩耍了。
getFragmentManager().beginTransaction()
.setCustomAnimations(enter, exit)
// 如果你有通过tag/id同时出栈多个Fragment的情况时,
// 请谨慎使用.setCustomAnimations(enter, exit, popEnter, popExit)
// 在support-25.4.0之前出栈多Fragment时,伴随出栈动画,会在某些情况下发生异常
// 你需要搭配Fragment的onCreateAnimation()临时取消出栈动画,或者延迟一个动画时间再执行一次上面提到的Hack方法,排序
(注意:如果你想给下一个Fragment设置进栈动画和出栈动画,.setCustomAnimations(enter, exit)只能设置进栈动画,第二个参数并不是设置出栈动画;
请使用.setCustomAnimations(enter, exit, popEnter, popExit),这个方法的第1个参数对应进栈动画,第4个参数对应出栈动画,所以是.setCustomAnimations(进栈动画, exit, popEnter, 出栈动画))
总结起来就是Fragment没有出栈动画的话,可以避免很多坑。
如果想让出栈动画运作正常的话,需要使用Fragment的onCreateAnimation
中控制动画。
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
// 此处设置动画
}
但是用代价也是有的,你需要解决出栈动画带来的几个坑。
1、pop多个Fragment时转场动画 带来的问题
6月17日更新: 在support-25.4.0版本,google意识到下面动画引起的问题,并修复了。
在使用 pop(tag/id)
出栈多个Fragment的这种情况下,将转场动画临时取消或者延迟一个动画的时间再去执行其他事务;
原因在于这种情景下,可能会导致栈内顺序错乱(上文有提到),同时如果发生“内存重启”后,因为Fragment转场动画没结束时再执行其他方法,会导致Fragment状态不会被FragmentManager正常保存下来。
2、进入新的Fragment并立刻关闭当前Fragment 时的一些问题
(1)如果你想从当前Fragment进入一个新的Fragment,并且同时要关闭当前Fragment。由于数据结构是栈,所以正确做法是先pop
,再add
,但是转场动画会有覆盖的不正常现象,你需要特殊处理,不然会闪屏!
Tip:
如果你遇到Fragment的mNextAnim空指针的异常(通常是在你的Fragment被重启的情况下),那么你首先需要检查是否操作的Fragment是否为null;其次在你的Fragment转场动画还没结束时,你是否就执行了其他事务等方法;解决思路就是延迟一个动画时间再执行事务,或者临时将该Fragment设为无动画
总结
看了上面的介绍,你可能会觉得Fragment有点可怕。
但是我想说,如果你只是浅度使用,比如一个Activity容器包含列表Fragment+详情Fragment这种简单情景下,不涉及到popBackStack/Immediate(tag/id)
这些的方法,还是比较轻松使用的,出现的问题,网上都可以找到解决方案。
但是如果你的Fragment逻辑比较复杂,有特殊需求,或者你的app架构是仅有一个Activity + 多个Fragment,上面说的这些坑,你都应该全部解决。
在下一篇中,介绍了一些非常实用的使用技巧,包括如何解决Fragment嵌套、各种环境、组件下Fragment的使用等技巧,推荐阅读!
还有一些比较隐蔽的问题,不影响app的正常运行,仅仅是一些显示的BUG,并没有在上面介绍,在本系列的最后一篇,我给出了我的解决方案,一个我封装的Fragmentation库,解决了所有动画问题,非常适合单Activity+多Fragment 或者 多模块Activity+多Fragment的架构。有兴趣的可以看看 :)
网友评论
这个例子没懂望大侠解释一下
怎么优化啊,难道是我fragment里的代码写多了? 还没有加数据请求 只是化UI 就卡了
for(int index=0;index<fragments.size();index++)
{
if(fragments.get(index) instanceof MainFragment)
{
getFragmentManager().popBackStack(fragments.get(index).getId(),FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}可是到了时间 没有返回MainFragment去 停在当前页面 请问 遇到这种情况 我应该怎么做?
fragment 嵌套 fragment 在切换 外层fragment之后 子fragment偶尔会出现重叠了。
Fragment是通过 show hide形式进行切换的,使用了动画之后 出现问题
进行切换之后,在一个界面点击自后界面会变成之前切换的界面
我pop并且push后,通过getFragments()获取到的List<Fragment>顺序(index从0开始)是:a,b,c,null; 我想查看一下真正栈的顺序,如何查看?是我这么获取不对吗?
话说,这个support是哪一个包啊?我在compile的时候没有找到25.4.0的v4包,有一个26.0.0-alpha1,但是里面也有“mAvailIndices”这个属性啊?并没有移除额= =!麻烦告知
url "https://maven.google.com"
}, 所以 repositories里添加如上即可, 26.0.0-a 应该是和25.4.0并行开发的版本,未来会合并的
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (outState != null) {
if (needRemoveFragmentState()) {
outState.remove("android:support:fragments");
outState.clear();
}
}
}
这个场景如何实现呢?如何判断此次这次事物是否提交呢?
正确做法是,执行事务时判断是否可见,不可见则设置标志位,在下次可见时在onPostResume()里执行事务
在最后感谢你写的这篇文章,我看了几遍, 感觉很有收获
发现这BUG的时候,我一脸懵比,幸好,stackoverflow上有大神给出了解决方案!hack FragmentManagerImpl的mAvailIndices,对其进行一次Collections.reverseOrder()降序排序,保证栈内Fragment的index的正确。
说 StackOverflow 大神提供了解决方案,可是我顺链接过去,发现和这里探讨的问题完全不一致,请问大神是否不小心附错了链接?如果是的话可否更正一下?感谢!
oooO ↘┏━┓ ↙ Oooo
( 踩)→┃你┃ ←(死 )
\ ( →┃√┃ ← ) /
\_)↗┗━┛ ↖(_/
的坑都踩了个遍,早能看到这么好的文章就好了,为了回复立马注册了一发。
1点击某个按钮时,可以EventBus发送一个Event,父Fragment/Activity 和 2 来监听该Event, 父接收后控制切换到2, 2接收后刷新
你可以通过开启 开发者模式->(滑动到最后)不保留最近活动 来模拟内存重启
根据友盟的错误日志看到是在按钮点击之后调用commit()触发的。想请教下这种情况应该怎么处理,谢谢
在离开AC时,比如进入下一个AC,或者回到桌面等,都立即会调用onSaveInstanceState(),在直到该AC重新onResume()期间,调用Fragment事务方法,必报此异常!
解决方法2个:
1、(不推荐)该事务使用commitAllowingStateLoss()方法提交,但是有可能导致该次提交无效!
2、(推荐)在重新回到该Activity的时候(比如onStart里),再执行该事务!
new Handler().postDelay(new Runnable(){
@Override
public void run() {
// 在这里执行Fragment事务
}
}, 你的出栈动画时间);
popBackStackImmdiate 不是同步的吗,为什么后面还要post
即时你的Fragment没有设置动画,我也是建议post()内执行的,在一些特殊情况可以保证安全。
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity) context;
}
我用getActivity()获取上下文程序正常,改成您说的这个就报空指针异常了.
V4里的Fragment现在有2个onAttach(): 2种写法略微不同
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof Activity){
this.mActivity = (Activity) context;
}else{
this.mActivity = getActivity();
}
}
或者
@Override
public void onAttach(Activity activity) {
super.onAttach(context);
this.mActivity = activity;
}
当前的Fragment重新打开一个Fragment然后快速点击回退,会出现当前Fragment中的控件点击无响应的状况,这是啥情况啊!求解
在App重启时 会导致mNextAnim的空指针,而我换为第一种
原文:
这里给出3个解决方案:
1、是大家比较熟悉的 findFragmentByTag:
就没有这个问题
不过现在还是推荐使用9行代码那个~一劳永逸的方法
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.chache:
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,tracke)
.hide(accessories).hide(message).hide(mine)
.commit();
break;
case R.id.accessories:
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,accessories)
.hide(tracke).hide(message).hide(mine)
.commit();
break;
case R.id.message:
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,message)
.hide(tracke).hide(accessories).hide(mine)
.commit();
break;
case R.id.mine:
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,mine)
.hide(tracke).hide(message).hide(accessories)
.commit();
break;
}
}
});
else{ // 正常时
tracke = TruckFragment.newInstance();
accessories = AccessoriesFragment.newInstance();
message=MessageFragment.newInstance();
mine=MineFragment.newInstance();
// // 这里add时,tag可传可不传
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,tracke)
.add(R.id.frame_content,accessories)
.add(R.id.frame_content,message)
.add(R.id.frame_content,mine)
.hide(accessories).hide(message).hide(mine)
.commit();
}
切换时:
getSupportFragmentManager().beginTransaction()
.show(mine) // add换成show
.hide(tracke).hide(message).hide(accessories)
.commit();
tost.showToast("内存重启");
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if (fragment instanceof TruckFragment) {
tracke = (TruckFragment) fragment;
} else if (fragment instanceof AccessoriesFragment) {
accessories = (AccessoriesFragment) fragment;
} else if (fragment instanceof MineFragment){
mine= (MineFragment) fragment;
} else if (fragment instanceof MessageFragment){
message= (MessageFragment) fragment;
}
// 解决重叠问题
getSupportFragmentManager().beginTransaction()
.show(tracke)
.hide(accessories)
.hide(message)
.hide(mine)
.commit();
}
}else{
// 正常时
tracke = TruckFragment.newInstance();
accessories = AccessoriesFragment.newInstance();
message=MessageFragment.newInstance();
mine=MineFragment.newInstance();
// // 这里add时,tag可传可不传
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_content,tracke)
.hide(accessories).hide(message).hide(mine)
.commit();
}
fragmentManager.beginTransaction()
.show(目标Fragment)
.hide(当前Fragment)
.commit();
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
这两句代码,pop多个Fragment有时会导致mAvailIndices里的下标顺序出问题,导致Fragment的index错误,附带mActive这个管理Fragment的List下标错误
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
onAttach(Context context)这个方法是新的support包里新加的,是为了更安全的保证有宿主Activity。其源码:
public void onAttach(Context context) {
mCalled = true;
final Activity hostActivity = mHost == null ? null : mHost.getActivity();
if (hostActivity != null) {
mCalled = false;
onAttach(hostActivity);
}
}
父Fragment.getChildFragmentManager().beginTransaction
.add(containerViewId,子Fragment)
...
你应该使用父Fragment的FrameLayout容器,作为containerViewId
排查下看看 这个我一直没遇到过,快速切换也测过,没遇到重叠问题
产生null异常的 大部分原因是在Fragment已经detach了宿主Activity,仍然去调用getActivity()时会发生空指针异常。
比如在pop了Fragment后,仍有异步任务在执行并调用了getActivity,或者在使用FragmentPagerAdapter时一些不恰当用法,以及在“内存重启”过程中,一些Fragment相关代码的不合理等都会导致getActivity空指针问题。
Fragment是官方提供的一个灵活的组件,在官方的app有大量的使用,有官方的支持,兼容性更好点,最重要的是官方会一直维护,任何Activity的新特性,Fragment基本也都会有的。