• 浅谈 Fragment 生命周期


    版权声明:本文为博主原创文章,未经博主允许不得转载。
    微博:厉圣杰
    源码:AndroidDemo/Fragment
    文中如有纰漏,欢迎大家留言指出。

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

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

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

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

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

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

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

    屏幕快照 2017-01-04 下午8.30.54

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

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

    项目结构如下
    屏幕快照 2017-01-04 下午8.43.37

    本文涉及到的类主要集中在 lifecircle 包中及 LifeCircleFragment。代码已上传 AndroidDemo/Fragment ,这里贴出 LifeCircleFragment 的主要代码。

    LifeCircleFragment.java

    /**
     * 测试 Fragment 生命周期,setUserVisibleHint 初始进来时只有默认 Tab
     * Created by littlejie on 2016/12/30.
     */
    
    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 尚未初始化完成。"
            //        + "
    你看不到我,因为我已经变成异常抛出了。");
            //}
        }
    
        /**
         * 创建 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 已初始化完成。"
                        + "
    你能看到我。");
            }
        }
    
        @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 已经初始化完成的。

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

    源码地址:AndroidDemo/Fragment。欢迎大家 Star 和 Fork。

  • 相关阅读:
    linux | 管道符、输出重定向
    php 升级php5.5 、php7
    mysql 启动失败
    centos7.2安装phpmyadmin
    php file_get_contents失败[function.file-get-contents]: failed to open stream: HTTP request failed!解决
    go println与printf区别
    前端 head 中mate 详解
    centos 7 安装mysql
    iOS数据持久化—数据库SQLite模糊查询
    C 语言字符串和格式化输入与输出
  • 原文地址:https://www.cnblogs.com/travellife/p/qian-tan-Fragment-sheng-ming-zhou-qi.html
Copyright © 2020-2023  润新知