正常在Activity中使用Fragment的生命周期,第一次启动过程是onAtach()-onCreate()-onCreateVIEw()-onVIEwCreated()-onActivityCreated()-onStart()-onResume();随着Activity被退栈销毁,Fragment的声明周期依次为onPause()-onStop()-onDestroyVIEw()-onDestroy()-onDetach();
如果在Fragment中使用EventBus等通过反射进行的 *** 作,在Fragment执行完onCreate()之后就会直接调用反射相关方法,由于还没有走onCreateVIEw()等方法创建视图,所以在反射相关方法中如果直接做UI层更新就会出现空指针异常等情况。这个BUG引导我开始关注FragmentManager的原理。
下面是BUG的使用场景:在一个Activity中创建AFragment和BFragment和一个相对应的两个button,在点击Abutton时显示AFragment,点击Bbutton是显示BFragment,同时在BFragment中执行相应 *** 作后,使用EventBus发送一个event,之后在Activity和AFragment中分别接收这个event,并执行相应 *** 作使得界面显示到AFragment并对AFragment中的数据进行更新。由于Activity中使用FragmentManager对两个Fragment进行切换,所以在AFragment接收event时有各种问题。原始未修复代码如下:
1 SecondActivity.class 2 @OverrIDe 3 protected voID onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 EventBus.getDefault().register(this); 6 setContentVIEw(R.layout.activity_second); 7 but1= (button) findVIEwByID(R.ID.but1); 8 but2= (button) findVIEwByID(R.ID.but2); 9 but1.setonClickListener(10 but2.setonClickListener(11 aFragment=AFragment.newInstance(null,null12 bFragment=BFragment.newInstance(13 changeFragment(aFragment);14 // addFragmentTransaction(aFragment);15 addFragmentTransaction(bFragment);16 17 }18 19 @Subscribe(threadMode = ThreadMode.MAIN)20 public handlerEvent(FragmentEvent event){21 Log.i(TAG,"SecondActivity.onMainThread: event="+event);22 23 24 25 private changeFragment(Fragment f){26 changeFragment1(f);27 28 29 changeFragment1(Fragment f){30 Log.i(TAG,"SecondActivity.changeFragment1: f="+f.getClass().getname());31 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();32 transaction.replace(R.ID.fl_container,f);33 transaction.addToBackStack(f.getClass().getname());34 transaction.commit();35 if(f.getClass().getname().equals(aFragment.getClass().getname())){36 bFragment.setUserVisibleHint(false37 aFragment.setUserVisibleHint(true38 }else {39 bFragment.setUserVisibleHint(40 aFragment.setUserVisibleHint(41 }42 }
1 AFragment. 2 private TextVIEw TitleTV; 4 5 6 Log.i(TAG,"AFragment.onCreate: " 7 8 if (getArguments() != ) { 9 mParam1 = getArguments().getString(ARG_ParaM1);10 mParam2 = getArguments().getString(ARG_ParaM2);11 12 EventBus.getDefault().register(14 16 public VIEw onCreateVIEw(LayoutInflater inflater,VIEwGroup container, Bundle savedInstanceState) {18 Log.i(TAG,"AFragment.onCreateVIEw: "19 Inflate the layout for this fragment20 return inflater.inflate(R.layout.fragment_a,container,1)">21 22 24 onVIEwCreated(VIEw vIEw,@Nullable Bundle savedInstanceState) {25 Log.i(TAG,"AFragment.onVIEwCreated: "26 .onVIEwCreated(vIEw,savedInstanceState);27 TitleTV=(TextVIEw)vIEw.findVIEwByID(R.ID.tv_fa_Title);28 29 30 31 onDestroy() {32 Log.i(TAG,"AFragment.onDestroy: "33 .onDestroy();34 EventBus.getDefault().unregister(35 36 @Subscribe(threadMode =37 38 Log.i(TAG,"AFragment.handlerFragmentEvent: event="+39 Log.i(TAG,"AFragment.handlerEvent: TitleTv="+TitleTV);40 TitleTV.setText(event.toString());41 }
1 BFragment.return inflater.inflate(R.layout.fragment_b,1)"> 9 10 12 13 14 TitleTV= (TextVIEw) vIEw.findVIEwByID(R.ID.tv_fb_Title);15 TitleTV.setonClickListener(new VIEw.OnClickListener() {16 @OverrIDe17 onClick(VIEw v) {18 Log.i(TAG,"BFragment.onClick: postEvent"19 EventBus.getDefault().post(new FragmentEvent(23));20 } });22 }
在Activity中使用FragmentManager进行切换布局,有两种方式,一是布局替换,即FragmentManager.replace()相关方法,第二种是布局显隐,即FragmentManager.hIDe()和FragmentManager.show()等相关方法。原始代码中使用replace()直接替换Fragment,这种方式导致每次替换的Fragment都是从onAttach()开始重新执行,所以之前在BFragment中发送event时,当前的AFragment还没有创建,也就不会执行对event的处理 *** 作。这样就有两种解决方案。
方案一:使用EventBus的poststicky()方法延缓发送event,而在AFragment中接收event时注解为sticky=true,默认为false。所以更新代码如下
1 AFragment.2 @Subscribe(threadMode = ThreadMode.MAIN,sticky = )3 4 Log.i(TAG,1)">5 Log.i(TAG,1)">6 7 }
5 TitleTV= 6 TitleTV.setonClickListener( 7 8 9 Log.i(TAG,1)">10 EventBus.getDefault().poststicky(12 13 }
使用方案一走日志发现在AFragment中的确已经正常接收到了event并对其进行了处理,但是处理event之后又从onCreateVIEw()开始执行更新视图等一系列 *** 作,导致event更新的视图被更新之后的视图所覆盖,处理这个问题的话,只要在handlerEvent中先不着急更新vIEw,而是新创建一个全局event来存储当前的event,在onVIEwCreated()中判断全局event如果非空就更新数据。方案一最终修改代码如下:
2 FragmentEvent mEvent; 3 @Subscribe(threadMode = ThreadMode.MAIN,1)"> 4 5 Log.i(TAG,1)"> TitleTV.setText(event.toString()); 8 mEvent=event;10 11 12 Log.i(TAG,1)">15 if(mEvent!=){ TitleTV.setText(mEvent.toString());18 }
方案二:使用FragmentManager的显隐方法而不是替换方法切换Fragment,使用hIDe()和show(),必须要先调用add()把Fragment添加到FragmentManager中,而此时切换两个Fragment,并不会重新执行onCreate()--onResume()等一系列流程,只有在Activity中显示调用Fragment的setUserVisibleHint()方法表示当前Fragment的显隐,说明Fragment已经绑定到Activity中,其生命周期只有在Activity的onResume()之中保存。方案二修改后的代码如下:
addFragmentTransaction(bFragment);18 addFragmentTransaction(Fragment fragment) {19 FragmentTransaction transaction = transaction.add(R.ID.fl_container,fragment);23 24 changeFragment2(f);25 26 27 changeFragment2(Fragment f){28 Log.i(TAG,"SecondActivity.changeFragment2: f="+29 FragmentTransaction transaction =30 31 transaction.hIDe(bFragment); transaction.show(aFragment);33 bFragment.setUserVisibleHint(34 aFragment.setUserVisibleHint(35 }36 transaction.hIDe(aFragment);37 transaction.show(bFragment);38 aFragment.setUserVisibleHint(42 }
以上两种解决方案各有优缺点,总体来说,如果想每次Fragment显示时都要重新更新VIEw,使用方案一的方式更好,但是方案一如果多次切换,由于之前的Fragment已经与当前Activity解除绑定,所以没有了引用的地方,但是仍然存在内存中,如果系统释放内存不及时,就会有内存泄漏的隐患;同样对于方案二来说,每次切换不会重新更新界面,所以如果想在显隐时做些 *** 作,只能显示调用Fragment的setUserVisibleHint()方法并重写该方法以做 *** 作。说到方案二的这种形式,和VIEwPager的相关切换方式有些相同,可参考。
总结以上是内存溢出为你收集整理的使用FragmentManager对Fragment的生命周期影响全部内容,希望文章能够帮你解决使用FragmentManager对Fragment的生命周期影响所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)