你真的了解Fragment的生命周期吗?
Android Framwork开发人员中的传奇人物Dianne Hackborn在2010年将Fragment引入了Android,也就是在android3.0之后引入Fragment,他在提交信息中写道:
“将单一的Activity拆分成多个独立的部件”的想法非常好。 然而,从今天Fragment的的实际使用效果来看,这一API的实现和演变并不理想。
虽然在项目中我们经常使用Fragment,但Fragment也有局限性和缺点,让我们来看下面这个流程图:
幸运的是,您在应用中使用Fragment的时候无需了解整个生命周期的转换过程,本文将介绍一些处理Fragment生命周期的方法,它隐藏了大部分的复杂细节。
Make Take On Fragment Lifecycle
处理Fragment生命周期的方法旨在实现两个目标:
1、使Fragment的生命周期处理逻辑尽可能类似于Activity的处理逻辑。
2、使Fragment的生命周期处理逻辑独立于Activity的生命周期。
使用类似于处理Activity的生命周期的方法来处理Fragment的生命周期,能大幅降低应用程序的整体复杂性;这意味着开发过程中的工作量会减少,维护会更容易,新团队成员熟悉项目的速度会更快,某种程度上,还可以减少bug产生的风险。
OnCreat(Bundle)
当Activity被强制销毁,之后又被自动恢复的时候,Android系统会在这一过程中销毁并重新创建Fragment。 重新创建的机制是通过使用反射的方法来调用Fragment的无参构造函数来实现的。 因此,如果您是使用带参数的构造函数来实例化Fragment,并在其中将依赖的对象传递给它,那么在保存和恢复后,所有这些依赖的对象都将被设置为null。
就像Activity一样,需要使用onCreate(Bundle)方法作为构造函数的替代者。 Fragment中依赖对象的注入和初始化就发生在这里;
与Activity的onCreate(Bundle)方法不同的是, 不得在Fragment的onCreate(Bundle)方法中执行任何与Android View相关的操作, onCreate(Bundle)方法在Fragment被Attach到Activity后仅被调用一次,它无法支持Fragment的View hierarchy的动态化;
将Activity转换为listener也发生在onCreate(Bundle)中,这将比在onAttach(Context)中抛出一个通用的ClassCastException要有意义的多;
View onCreatView(LayoutInflater,ViewGroup,Bundle)
这个方法是Fragment独有的,这是它与Activity生命周期最显着的区别。
Activity在生命周期的转换过程中都只有同一个View hierarchy。 你在Activity的onCreate(Bundle)中初始化这个View hierarchy,然后它就会一直存在于Activity的整个生命周期,直到Activity被垃圾收集器回收为止;
然而,在Fragment在其生命周期中可以存在有多个View hierarchy,由Android系统决定何时进行替换;
每次需要创建新的View hierarchy的时候,Android系统都会调用onCreateView(LayoutInflater, ViewGroup, Bundle)方法;
重写这个方法的主要原则是:Fragment中所有持有与View hierarchy相关的对象的引用的成员变量,必须在View onCreateView(LayoutInflater,ViewGroup,Bundle)中进行初始化,如果Fragment的成员变量持有View或者相关对象的引用,须确保在此方法中初始化这些成员变量;
方法的基本的处理逻辑如下:
Fragment里每个与View hierarchy相关的成员变量都必须在此方法中初始化, 这包括列表适配器,用户交互事件的监听器等。保持Fragment里的代码可维护性的唯一方法是确保此方法在重新创建整个View hierarchy的时候会重新初始化Fragment里与之相关的成员变量,这也是为了减小内存泄漏的风险。
onStart()
Fragment的这个方法与Activity的onStart()方法具有完全相同的职责和指导原则,方法的基本的处理逻辑如下:
@Override public void onStart() { super.onStart(); mSomeView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handleOnSomeViewClick(); } }); mFirstDependency.registerListener(this); switch (mSecondDependency.getState()) { case SecondDependency.State.STATE_1: updateUiAccordingToState1(); break; case SecondDependency.State.STATE_2: updateUiAccordingToState2(); break; case SecondDependency.State.STATE_3: updateUiAccordingToState3(); break; } if (mWelcomeDialogHasAlreadyBeenShown) { mFirstDependency.intiateAsyncFunctionalFlowAndNotify(); } else { showWelcomeDialog(); mWelcomeDialogHasAlreadyBeenShown = true; } }
这个方法里包含了Fragment的大部分功能逻辑。 保持onStart()方法在Activity和Fragment里的一致性具有很多好处。
onResume()
这个方法的处理逻辑与Activity的onResume()方法相同。
onPause()
这个方法的处理逻辑与Activity的onResume()方法相同。
onStop()
这个方法的处理逻辑同样与Activity的onStop()方法相同,在此方法中,我们可以注销点击事件的监听器,不是必须的,但是也可以减少bug的风险。
onDestroyView()
之前说必须在onCreateView(LayoutInflater,ViewGroup,Bundle)里初始化Fragment里所有持有View或者相关对象的引用的成员变量。这个要求源自于这样一个事实:Fragment的View hierarchy可以被重新创建,所以所有未在该方法中被初始化的持有View的引用对象都将被置为null;
如果这样做了,Fragment将一直持有对这些View的强引用,直到下一次调用View onCreateView(LayoutInflater,ViewGroup,Bundle)方法或者整个Fragment被销毁;
有很多建议是应该在onDestroyView()方法里将所有前面提到的成员变量设置为Null。 目的是尽快释放这些引用以允许垃圾收集器在onDestroyView()返回后立即回收它们,这样就能更快地释放与这些View相关的内存空间,但这是过早优化的经典案例,在绝大多数情况下,你并不需要这种优化,所有,我们不需要重写onDestroyView()方法。
onDestroy()
像Activity一样,您不需要在Fragment中重写此方法。
onSaveInstanceState(Bundle)
这个方法的基本的处理逻辑如下:
这个方法对应于Activity的onSaveInstanceState(Bundle)方法,所以对Activity的onSaveInstanceState(Bundle)的大多数讨论也适用于此。但请注意:此方法可能在onDestroy()之前的任何时候被调用;在许多情况下,Fragment可能会被销毁。
根据官方文件:这个方法可能在onDestroy()之前的任何时候调用。 这里有两个主要问题:
1、与Fragment相关联的View hierarchy可以被销毁而不实际保存其状态。 因此,如果您想在View onCreateView(LayoutInflater,ViewGroup,Bundle)中恢复Fragment的状态,这会加大程序崩溃的几率;
2、如果在onDestroy()之前的任何时候都可以调用onSaveInstanceState(Bundle),那么就无法保证何时才能安全地更改Fragment的状态(例如替换嵌套的Fragment)。
setRetainInstance(boolean)
Fragment具有属性retainInstance,默认值为false。
当设备旋转时,fragment会随托管activity一起销毁并重建。
调用setRetainInstance(true)方法可保留fragment,代码如下:
已保留的fragment不会随着activity一起被销毁;
相反,它会一直保留(进程不消亡的前提下),并在需要时原封不动地传递给新的Activity。
但是此方法建议不使用,因为此方法改变了Fragment的生命周期。
为什么Fragment如此复杂?
也许引入这种机制是为了优化内存的消耗。当Fragment不可见的时候,销毁Fragment的View hierarchy允许释放一些内存。 但另一个问题是:Fragment实现了对状态保存和恢复流程的支持;
在FragmentStatePagerAdapter并不是采用这种机制去保存和恢复Fragments的状态。
借用原作者的话:整个机制可能是一种过早的优化,它没有任何用处,反而会使得Fragment使用更复杂。
结语
虽然Fragment,整个机制复杂,但是与“一个界面一个Activity”的方法相比,Fragment可以提供更好的用户体验;
但是我们在使用Fragment的时候,建议在处理Fragment的生命周期时让它类似于Activity的生命周期并且独立于它,这使得Fragment的复杂性是可以管理的。
小伙伴们,你的支持是我前进的动力,动动手指,扫面二维码关注我们吧,如果您有任何补充或者想要指出本文中的任何缺陷,请给小编留言哦。