继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

你真的了解Fragment的生命周期吗?

Cats萌萌
关注TA
已关注
手记 275
粉丝 50
获赞 304

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

原文链接:http://www.apkbus.com/blog-918160-78273.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP