你真的了解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的复杂性是可以管理的。

 

小伙伴们,你的支持是我前进的动力,动动手指,扫面二维码关注我们吧,如果您有任何补充或者想要指出本文中的任何缺陷,请给小编留言哦。

 

posted @ 2018-07-10 14:17  zhengbang  阅读(2747)  评论(0编辑  收藏  举报