深入理解Fragment进阶系列(一):生命周期详解

来源:互联网 发布:galgame翻译软件 编辑:程序博客网 时间:2024/06/07 08:16

深入理解Fragment进阶系列(一):生命周期详解

本系列是介绍Fragment相关的一些内容,由浅入深。

1. 深入理解Fragment进阶系列(一):生命周期详解

Fragment最初出现在Android3.0,用来解决大小屏设备适配问题。Fragment不能单独存在,必须依附于Activity(Android4.2起Fragment也可以嵌套Fragment)。Fragment有一套自己的生命周期,相应的也是依赖Activity的生命周期( 例如,当 Activity 暂停时,其中的所有Fragment也会暂停;当 Activity 被销毁时,所有Fragment也会被销毁)。一个Activity可以使用多个Fragment,一个Fragment也可以附属在多个Activity上,十分灵活。

对于低版本可以使用V4库,需要注意的是使用V4的Fragment,Activity要继承FragmentActivity,获取FragmentManger要使用getSupportFragmentManger()。

既然Fragment拥有自己的生命周期,本文就来详细的讲一讲相关的内容。主要包括以下几个方面。

  1. Fragment的生命周期
  2. Activity 生命周期对Fragment生命周期的影响
  3. Fragment相关操作对生命周期的影响
  4. 应用被系统回收对生命周期的影响
  5. ViewPager对Fragment生命周期的影响
  6. 判断Fragment是否可见

Fragment的生命周期

说起Fragment的生命周期,就不得不放一张十分经典的官方图。
片段的生命周期(其 Activity 运行时)

emmmm… 乍一看是不是有一张似曾相识的感觉,Fragment的生命周期大体上和Activity类似,我们先对每个回调方法都具体的介绍一下。

Fragment的生命周期是和其关联的Activity有关,在这里为了方便叙述之后说的Activity都特指Fragment相关联的Activity

  1. onAttach(Context context):在Fragment和Activity关联上的时候调用,且仅调用一次。在该回调中我们可以将context转化为Activity保存下来,从而避免后期频繁调用getAtivity()获取Activity的局面,避免了在某些情况下getAtivity()为空的异常(Activity和Fragment分离的情况下)。同时也可以在该回调中将传入的Arguments提取并解析,在这里强烈推荐通过setArguments给Fragment传参数,因为在应用被系统回收时Fragment不会保存相关属性,具体之后会讲解。

  2. onCreate:在最初创建Fragment的时候会调用,和Activity的onCreate类似。

  3. View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState):在准备绘制Fragment界面时调用,返回值为Fragment要绘制布局的根视图,当然也可以返回null。注意使用inflater构建View时一定要将attachToRoot指明false,因为Fragment会自动将视图添加到container中,attachToRoot为true会重复添加报错。onCreateView并不是一定会被调用,当添加的是没有界面的Fragment就不会调用,比如调用FragmentTransaction的 add(Fragment fragment, String tag)方法。

  4. onActivityCreated :在Activity的onCreated执行完时会调用。

  5. onStart() :Fragment对用户可见的时候调用,前提是Activity已经started。

  6. onResume():Fragment和用户之前可交互时会调用,前提是Activity已经resumed。

  7. onPause():Fragment和用户之前不可交互时会调用。

  8. onStop():Fragment不可见时会调用。

  9. onDestroyView():在移除Fragment相关视图层级时调用。

  10. onDestroy():最终清楚Fragment状态时会调用。

  11. onDetach():Fragment和Activity解除关联时调用。

图中有一条从onDestroyView指向onCreatedView的线,意思是Fragment从back栈(存放Fragment的栈)回到前台时经历的生命周期。


Activity 生命周期对Fragment生命周期的影响

将Fragment生命周期中每个回调单独讲完之后,下面就来看看Fragment生命周期和Activity生命周期之前的联系,这里也有一张十分经典的官方图。

Activity 生命周期对片段生命周期的影响

可以十分明了的看到,当Activity处于不同生命周期时Fragment生命周期的流程。需要关注一下两者生命周期顺序问题,其中onCreate、onStart、onResume都是Activity先调用之后才是Fragment,onPause、onStop、onDestroy(在Fragment中是onDetach),是先Fragment调用之后才是Activity。其他的就不展开说了,图十分明了。


Fragment相关操作对生命周期的影响

添加Fragment可以分为静态添加和动态添加两大类。静态添加是在XML中直接添加Fragment,简单方便,缺点是添加之后不能在删除。动态添加是在代码中FragmentManger使用一系列FragmentTransaction事务操作动态控制,灵活多变。一般都是使用动态添加,下面就讲讲动态添加有关的生命周期。

  1. add:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume

  2. remove:onPause->onStop->onDestroyView->onDestroy->onDetach

  3. show:onHiddenChanged(boolean hidden) hidden为false

  4. hide:onHiddenChanged(boolean hidden) hidden为true

  5. replace:旧Fragment的remove生命周期->新Fragment的add生命周期

  6. replace+addToBackStack:onPause->onStop->onDestroyView->新Fragment的add生命周期
    之后点击back:新Fragment的remove->onCreateView->onViewCreated->onActivityCreated->onStart->onResume 就是第一张图的线

  7. detach:onPause->onStop->onDestroyView 可以看到只是视图被移除,Fragment关联状态还是不变,还是处于FragmentManger的管理下

  8. FragmentTransaction.attach(Fragment var1):onStart->onResume->onCreateView

注意:Fragment的show和hide仅仅是将Fragment视图设置为是否可见,不会调用任何生命周期。该Fragment的生命周期还是会随着Activity的生命周期变化而变化,例如FragmentA hide、FragmentB show,点击Home A和B都会onPause->onStop


应用被系统回收对生命周期的影响

应用被回收一般都是后台应用,所以生命周期是从onDestroyView开始

  • 单独一个Fragment
    onDestroyView->onDestroy->onDetach->add生命周期

  • Fragment A hide,Fragment B show
    A.onDestroyView->A.onDestroy->A.onDetach->B.onDestroyView->B.onDestroy->B.onDetach->A.onAttach->A.onCreate->B.onAttach->B.onCreate->A.onCreateView->A.onActivityCreated->B.onCreateView->B.onActivityCreated->A.onStart->B.onStart->A.onResume->B.onResume

为了防止在系统回收应用情况下,再次进不出错,强烈建议大家使用setArguments(Bundle bundle)方法传递参数。对于常规变量想必大家都已经十分熟练了,就不细说了。这里主要强调View变量和接口变量,View变量可以通过传入View的id,之后再通过id获取view的方法来实现。接口可以通过Activity实现,Fragment强转Activity实现。


ViewPager对Fragment生命周期的影响

ViewPager+Fragment已经是比较常见的组合,一般搭配ViewPager的FragmentPagerAdapter或FragmentStatePagerAdapter使用。不过ViewPager为了防止滑动出现卡顿,有一个缓存机制,默认情况下ViewPager会创建并缓存当前页面左右两边的页面(如Fragment)。此时左右两个Fragment都会执行从onAttach->….->onResume的生命周期,明明Fragment没有显示却已经到onResume了,在某些情况下会出现问题。比如数据的加载时机、判断Fragment是否可见等。

可以通过ViewPager的setOffscreenPageLimit(int limit)来设置ViewPager缓存个数,最小是1个即便我们设置为0,源码如下。

 public void setOffscreenPageLimit(int limit) {        if (limit < DEFAULT_OFFSCREEN_PAGES) {         //     private static final int DEFAULT_OFFSCREEN_PAGES = 1;            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "                    + DEFAULT_OFFSCREEN_PAGES);            limit = DEFAULT_OFFSCREEN_PAGES;        }        if (limit != mOffscreenPageLimit) {            mOffscreenPageLimit = limit;            populate();        }    }

懒加载

如果不对数据加载时机做处理,使用ViewPager默认会将左右两个Fragment的数据也加载,当数据加载比较消耗资源会影响性能。我们可以对加载时机做调整使用懒加载,具体就是在Fragment真正可见时才加载数据。

在Fragment切换时候会调用setUserVisibleHint(boolean isVisibleToUser),isVisibleToUser表示是否对用户可见。setUserVisibleHint不单单在Fragment切换时调用,在onAttach之前都会调用一次此时isVisibleToUser为false,在onCreateView之前会调用一次此时isVisibleToUser的值为当前Fragment是否可见,之后就是在Fragment切换的时候会调用。

因为setUserVisibleHint会在onCreateView之前调用,如果数据加载涉及到view相关的操作别忘了设置一个变量来判断一下视图是否已经创建好。


判断Fragment是否可见

判断Fragment是否可见最常见就是用在埋点上了,不同情况下判断方法并不相同,主要分为两大类。

在Activity中使用

先上一段在Fragment可见的埋点代码

 @Override    public void onResume() {        super.onResume();        // 注意这个判断 isHidden()是当前Fragment是否隐藏        if (!isHidden()) {            reportPageShow();        }    }@Override    public void onHiddenChanged(boolean hidden) {        super.onHiddenChanged(hidden);        if (!hidden) {            reportPageShow();        }    }

在添加Fragment或者从Fragment和其他Activity相互跳转的时候,都会调用onResume和onStop。如果是show或在hide Fragment的时候会调用onHiddenChanged。

不知道大家有没有发现在onResume的时候判断了一下当前Fragment是否被隐藏了,这个判断是有说法的。之前说过show和hide仅仅是将Fragment视图设置为是否可见,不会影响其生命周期。还原场景,如果Fragment A hide,Fragment B show,然后Activity从后台到前台是A和B的onResume都会被调用!所以要在onResume判断一下当前Fragment是否被隐藏,当然如果没有用到show和hide,只使用简单的onResume和onStop也没问题。

在ViewPager中使用

之前说过ViewPager为了防止滑动出现卡顿,有一个缓存机制,默认情况下ViewPager会创建并缓存当前页面左右两边的页面(如Fragment)。左右两个Fragment都会执行从onAttach->….->onResume的生命周期,此时Fragment的生命周期已经不可靠。不过在Fragment切换的时候会调用setUserVisibleHint(boolean isVisibleToUser),isVisibleToUser表示是否对用户可见。因此可以在setUserVisibleHint进行Fragment是否可见的判断。

 @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if (isVisibleToUser) {            reportPageShow();        }    }

看完上面的内容想必对Fragment的生命周期了解有所加深,下面一篇将会从源码来分析一下Fragment相关的内容