Android Fragment生命周期

御弟哥哥 等级 636 0 1

Fragment 是在 Android 3.0 中引入,用于解决不同屏幕分辨率的设备上 UI 显示、交互的问题。Fragment 有自己的布局,有自己的生命周期,有自己的事件响应。

但 Fragment 又是依赖于 Activity 存在的,你可以把多个 Fragment 嵌入到一个 Activity 中或者多个 Activity 重用一个 Fragment。Activity 的生命周期直接影响 Fragment 的生命周期。所以要正确的使用 Fragment,首先必要从根本上了解 Fragment 的生命周期,俗话说:工欲善其事必先利其器

简单来说,Fragment 的生命周期可以用下图来表示:

Android Fragment生命周期

fragment_lifecycle

是不是觉得这图看着很眼熟,没错,前面讲过 Fragment 是依赖于 Activity 存在的,所以 Fragment 的生命周期跟 Activity 的生命周期很相似。

下图很好的描述了 Fragment 与 Activity 生命周期的关系,请看图~

Android Fragment生命周期

fragment_and_activity_lifecycle

Fragment 的生命周期这样就讲完了?理论上来说,是的。我说楼主,你还真是浅谈别急,下面还有呢

上面只是展示了 Fragment 与 Activity 生命周期最基本的关系,如果通过 addToBackStack() 将 Fragment 放入回退栈,然后通过 popBackStack() 出栈,Fragment 的生命周期会如何变化呢?如果 Fragment 与 ViewPager 结合使用,Fragment 的生命周期又是如何?如果通过 hide()show() 方法来展示隐藏,这时 Fragment 的生命周期又会如何?

不急,先看思维导图中的问题,然后咱们就来研究一下上诉问题~

既然是 Fragment 的生命周期,那自然是少不了对 Fragment 生命周期的监测,怎么办?打 Log。

LifeCircleFragment.java

/**
 * 测试 Fragment 生命周期,setUserVisibleHint 初始进来时只有默认 Tab
 */

public class LifeCircleFragment extends BaseFragment {

    private final String TAG = LifeCircleFragment.class.getSimpleName();
    //截取 Fragment.toString() 方法中的标识数字
    private final String ID = this.toString().substring(this.toString().indexOf("{") + 1, this.toString().length() - 1);
    private TextView mTvContent;

    //默认 Title 值
    private String mTitle = "Tab";

    public static LifeCircleFragment newInstance(String title) {
        Bundle args = new Bundle();

        LifeCircleFragment fragment = new LifeCircleFragment();
        args.putString(Constant.PARAM_TITLE, title);
        fragment.setArguments(args);
        return fragment;
    }

    /**
     * 当 Fragment 调用 hide() 、 show() 时回调
     * @param hidden
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onHiddenChanged.hidden = " + hidden);
        super.onHiddenChanged(hidden);
    }

    /**
     * 当 Fragment 与 ViewPager 结合使用时,切换 Pager 时回调
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is setUserVisibleHint.isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
    }

    /**
     * Fragment 关联到 Activity 时回调
     * 此时 Activity 已经与 Fragment 关联,通过 Context 向下转型,就可以与 Activity 通信
     * 当然也可以使用 getActivity(),前提是这个 fragment 已经和宿主的 activity 关联,并且没有脱离
     * onAttach 只调用一次。
     *
     * @param context
     */
    @Override
    public void onAttach(Context context) {
        //由于 onCreate 是在 onAttach 后执行,故此时 mTitle 为空
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onAttach.");
        super.onAttach(context);
    }

    /**
     * 系统创建 Fragment 的时候回调,介于 onAttach() 和 onCreateView() 之间
     * 一般用于初始化一些数据
     * 值得注意的是,此时 Activity 还在创建中,因此不能在执行一些跟 Activity UI 相关的操作
     * 否则,会出现一些难以预料的问题,比如:NullPointException
     * 如果要对 Activity 上的 UI 进行操作,建议在 onActivityCreated() 中操作
     *
     * @param savedInstanceState
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onCreate.");
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        if (getArguments() != null) {
            mTitle = getArguments().getString(Constant.PARAM_TITLE);
        }
        //测试 onCreate() 是 Activity 的 UI 是否初始化完成
        //if (getContext() instanceof LifeCircleActivity) {
        //((LifeCircleActivity) getContext()).setActivityCreated("Fragment 进行 onCreate() 时 Activity UI 尚未初始化完成。"
        //        + "\n你看不到我,因为我已经变成异常抛出了。");
        //}
    }

    /**
     * 创建 Fragment 需要显示的 View,默认返回 null。
     * 当返回的 View 不为 null 时,View 被销毁时会调用 onDestroyView()
     * 此处应该只进行布局的初始化,而不应该执行耗时操作,如网络请求、数据库读取
     *
     * @param inflater
     * @param container
     * @param savedInstanceState
     * @return
     */
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onCreateView.");
        return inflater.inflate(R.layout.fragment_life_circle, container, false);
    }

    /**
     * 该方法在 onCreateView() 之后会被立即执行
     * 此时可以对 View 对象进行赋值,当然在 onCreateView() 中也可以执行
     *
     * @param view
     * @param savedInstanceState
     */
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onViewCreated.");
        super.onViewCreated(view, savedInstanceState);
        mTvContent = (TextView) view.findViewById(R.id.tv_content);
        mTvContent.setText(mTitle);
    }

    /**
     * 当 Activity 执行完 onCreate() 方法后调用
     * 此时可以执行与 Activity 相关的 UI 操作
     *
     * @param savedInstanceState
     */
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onActivityCreated.");
        super.onActivityCreated(savedInstanceState);
        //测试 onCreate() 是 Activity 的 UI 是否初始化完成
        if (getContext() instanceof SimpleLifeCircleActivity) {
            ((SimpleLifeCircleActivity) getContext()).setActivityCreated("Fragment 进行 onActivityCreated() 时 Activity UI 已初始化完成。"
                    + "\n你能看到我。");
        }
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onViewStateRestored.");
        super.onViewStateRestored(savedInstanceState);
    }

    /*-----------跟 Activity 中对应方法类似-------------*/
    @Override
    public void onStart() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onStart.");
        super.onStart();
    }

    @Override
    public void onResume() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onResume.");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onPause.");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onStop.");
        super.onStop();
    }
    /*-----------跟 Activity 中对应方法类似-------------*/

    /**
     * 表示销毁 Fragment 相关联的 UI 布局,清除所有跟视图相关的资源。
     * 不一定在 Activity 的 onDestroy() 方法中调用
     * 如:Fragment 与 ViewPager 结合使用时
     *
     * @see com.littlejie.fragment.lifecircle.LifeCircleWithViewPagerActivity
     */
    @Override
    public void onDestroyView() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroyView.");
        super.onDestroyView();
    }

    /**
     * 销毁 Fragment 对象的时候调用,一般是调用 Activity 的 onDestroy() 的时候执行
     */
    @Override
    public void onDestroy() {
        //当调用 Activity 的 onDestroy() 时调用
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDestroy.");
        super.onDestroy();
    }

    /**
     * 解除 Fragment 与 Activity 的关联
     */
    @Override
    public void onDetach() {
        Log.i(TAG, "Fragment id = " + ID + "," + mTitle + " is onDetach.");
        super.onDetach();
    }

} 

由于日志过多,这里不再展示,有兴趣的可自行下载 Demo 测试。这里简要总结下关于 Fragment 生命周期。

  1. 第一点很重要,最开始的两张图展示的 Fragment 与 Activity 的生命周期关系没毛病。
  2. onAttach()onCreate() 只在 Fragment 与 Activity 第一次关联时调用。
  3. onDestroy()onDetach() 只在 Fragment 的宿主 Activity 销毁时才会被调用。
  4. 根据前 3 点,将 Fragment 通过 addToBackStack() 只涉及 onCreateView()onDestroyView() 这之间的生命周期。add()replace() 不会对 Fragment 的生命周期产生影响,但 add() 方法会造成 Fragment 叠加显示。
  5. Fragment 与 ViewPager 结合使用时的生命周期与第 4 点相似。
  6. 通过 hide()show() 来隐藏、显示Fragment,此时 Fragment 只改变了可见性,并不涉及生命周期的改变
  7. 第 7 点与Fragment 和 Activity 的生命周期有关,即:不要在 Fragment 的 onCreate() 方法中操作宿主Activity 的 UI。因为你无法保证此时 宿主Activity 的 UI 已经完全初始化。PS:某些情况下是可以确保 宿主Activity 已经初始化完成的。

恩,的确算浅谈,给出了自认为正确的结论,欢迎各位前来打脸~

收藏
评论区

相关推荐

Android Activity生命周期,启动模式,启动过程详解
前言 接触过Android开发的同学都知道Activity,Activity作为Android四大组件之一,使用频率高。简单来说Activity提供了一个显示界面,让用户进行各种操作,本文主要分为以下三个部分:Activity的生命周期,启动模式,以及Activity的工作过程。文中大部分篇幅来自《Android开发艺术探索》一书,尽管想多以流程或图
Android中一个Activity关闭另一个Activity或者在一个Activity中关闭多个Activity
前言 最近项目中涉及需要在一个Activity中关闭另一个Activity或者在一个Activity中关闭多个Activity的需求,不涉及到应用的退出。自己首先想了一些方案,同时也查了一些方案,就各个方案比较下优劣。 方案一 广播的方式 这个是最容易想到的,同时也是网上提供最多的。 由于多个Activity要使用,关闭页面
Android通过URL打开Activity
关注公众号 QXF069 为每个 Activity 绑定一个 url 可以方便的让第三方 app 直接打开这些 Activity。也可以方便在 app 内部进行页面跳转,解耦。 背景 举一个常见的案例,假设我们有个产品 A,产品 A 包含 h5 网页端和客户端,当用户在手机打开我们的 h5 网页端的时候,我们会期望如果用户手机安装了我们的客户端,则直接打
Android Fragment生命周期
Fragment 是在 Android 3.0 中引入,用于解决不同屏幕分辨率的设备上 UI 显示、交互的问题。Fragment 有自己的布局,有自己的生命周期,有自己的事件响应。 但 Fragment 又是依赖于 Activity 存在的,你可以把多个 Fragment 嵌入到一个 Activity 中或者多个 Activity 重用一个 Fragmen
Android ViewPager缓存原理分析
前言: 此篇文章讲述了viewpager的基本使用,以及解决和分析刷新不及时的问题,最后是项目中遇到的bug总结,希望对你们有所帮助 一.ViewPagerFragment的使用 第一步:创建几个fragment 第二步:实例化ViewPager,添加Adapter 第三步:传值绑定 public class MainActivity e
Android -- Fragment 基本用法、生命周期与细节注意
引言:这篇文章,大概分析下Fragment的生命周期、实际应用方法以及使用Fragment时需要注意的地方,算是Fragment的入门级文章,理解透Fragment生命周期和一些细节,就容易理解Fragment如何与外界通信等问题了。至于对其的源码分析等更加深入的内容,本文涉及不多。 Fragment的写法就不多说了,一般是继承Fragment,然后重
LayoutInflater.inflate()参数用法及导致适配器布局宽度高度错乱问题
这个LayoutInflater.inflate()应该用的都挺频繁的,比如你的fragment,你的适配器里面都会有用到。但它的参数的意义你都理解嘛? 有没有遇到过这样一个问题?你的适配器宽度明明设置了全部但是实际上却没有,布局错乱了,然后你苦寻无果,最后你直接在代码中动态重新设置了一次宽度,获取屏幕的宽度在代码中动态直接设置。 今天我们就来解释一
Android深入四大组件(一)应用程序启动过程(前篇)
Android框架层 Android深入四大组件categories: Android框架层本文首发于微信公众号「后厂技术官」 前言在此前的文章中,我讲过了Android系统启动流程和Android应用进程启动过程,这一篇顺理成章来学习Android 7.0的应用程序的启动过程。分析应用程序的启动过程其实就是分析根Activity的启动过程。<!more 1
Android深入四大组件(六)Android8.0 根Activity启动过程(前篇)
Android框架层 Android深入四大组件categories: Android框架层本文首发于微信公众号「刘望舒」 前言在几个月前我写了和这两篇文章,它们都是基于Android 7.0,当我开始阅读Android 8.0源码时发现应用程序(根Activity)启动过程照Android 7.0有了一些变化,因此又写下了本篇文章,本篇文章照此前的文章不仅
Android深入四大组件(七)Android8.0 根Activity启动过程(后篇)
Android框架层 Android深入四大组件categories: Android框架层本文首发于微信公众号「刘望舒」 前言在几个月前我写了和这两篇文章,它们都是基于Android 7.0,当我开始阅读Android 8.0源码时发现应用程序(根Activity)启动过程照Android 7.0有了一些变化,因此又写下了本篇文章,本篇文章照此前的文章不仅
Android解析ActivityManagerService(二)ActivityTask和Activity栈管理
Android框架层 Android系统服务 ActivityManagerService Android框架层本文首发于微信公众号「刘望舒」 前言关于AMS,原计划是只写一篇文章来介绍,但是AMS功能繁多,一篇文章的篇幅远远不够。这一篇我们接着来学习与AMS相关的ActivityTask和Activity栈管理。 1.ActivityStackActivi
Android Fragment 从源码的角度去解析(下)
1.概述 上一篇博客已经简单的讲了一下Fragment的使用并写了一个基本的实例,接下来就将其整合到项目中。附视频地址:      这里写图片描述 2.效果实现 列表和轮播条不做过多的解释就是访问接口获取数据而已,这个在和都讲过了。我们直接整合进去这个时候我们发现一个奇怪的问题,就是切换之后会去重新加载数据很不正常。   一般的思路我们会换实现方法
打造炫酷通用的ViewPager指示器 - 玩转字体变色
1.概述 最近一直都在仿着其他项目的效果在做,仿内涵段子,二手车之家等等。会不会有一天被抓还真是有点心虚,我这分明是给这些APP打广告。等这些效果基本讲完就开始设计模式和系统框架一整套的视频也就会出来了,等总的访问量达到100万之后就会利用空余的时间去录制Java基础和Android基础,请各位多多start和suggest。我们来看一下效果:   
满满干货!高级Android都应该知道
AndroidX的前世今生Android系统在刚刚面世的时候,可能连它的设计者也没有想到它会如此成功,因此也不可能在一开始的时候就将它的API考虑的非常周全。随着Android系统版本不断地迭代更新,每个版本中都会加入很多新的API进去,但是新增的API在老版系统中并不存在,因此这就出现了一个向下兼容的问题。举个例子,当Android系统发布到3.0版本的时
这是一份用心整理的Android面试总结,涨姿势!
Android Jetpack组件的作用是什么? Navigation:一个用于管理Fragment切换的工具类,可视化、可绑定控件、支持动画等是其优点。 Data Binding:不用说,都知道,加速MVVM的创建。 Lifecycle:他是我们能够处理Activity和Fragment的生命周期的重要原因,在AndroidX的Fragment和Activ