使用FragmentManager对Fragment的生命周期影响

使用FragmentManager对Fragment的生命周期影响,第1张

概述正常在Activity中使用Fragment的生命周期,第一次启动过程是onAtach()-onCreate()-onCreateView()-onViewCreated()-onActivityCr

  正常在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的生命周期影响所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1119343.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-29
下一篇 2022-05-29

发表评论

登录后才能评论

评论列表(0条)

保存