Fragment 生命周期浅析及常见问题整理

Author Avatar
dev.liang 4月 02, 2019
  • 在其它设备中阅读本文章

平时项目中少不了 Fragment 的使用,也会遇到一些奇怪的问题,今天抽时间对 Fragment 的生命周期,奇葩问题进行一个稍微全面的总结 官方指南

Fragment 各个生命周期作用和拓展

Fragment 各个生命周期介绍

先上两张官方图,左侧 Activity 和 Fragment 生命周期对比,右侧是 Fragment 生命周期。

activity and fragment lifecyclefragment_lifecycle

onAttach()

执行该方法时,Fragment 与 Activity 已经完成绑定,onAttach (Activity activity) 该方法有一个 Activity 类型的参数,代表绑定的 Activity,这时候你可以根据需要进行 mActivity = activity 赋值操作。

拓展:Fragment 关联到 Activity 时的回调,此时 Activity 已经与 Fragment 关联,通过 Context 向下转型,就可以与 Activity 通信当然也可以使用 getActivity(),前提是这个 fragment 已经和宿主的 activity 关联,并且没有脱离。onAttach 只调用一次。

onCreate()

初始化Fragment。可通过参数 savedInstanceState 获取之前保存的值。

拓展:系统创建 Fragment 的时候回调,介于 onAttach() 和 onCreateView() 之间,一般用于初始化一些数据,需要注意的是,此时 Activity 还在创建中,因此不能在执行一些跟 Activity UI 相关的操作,否则会出现一些难以预料的问题,如NullPointException 如果要对 Activity 上的 UI 进行操作,可在 onActivityCreated() 中操作。

onCreateView()

初始化 Fragment 的布局。加载布局和 findViewById 的操作通常在此函数内完成,但是不建议执行耗时的操作,比如读取数据库数据列表。

拓展:创建 Fragment 需要显示的 View,默认返回 null。当返回的 View 不为 null 时,View 被销毁时会调用onDestroyView(),此处应该只进行布局的初始化,而不应该执行耗时操作,如网络请求、数据库读取等。另外有时候 onCreateView 会被多次调用, 可以把初始化布局、view 的过程放在 onCreate 中,onCreateView 中直接 return View 对象即可。

onActivityCreated()

执行该方法时,与 Fragment 绑定的 Activity 的 onCreate 方法已经执行完成并返回,在该方法内可以进行与 Activity 交互的 UI 操作,所以在该方法之前 Activity 的 onCreate 方法并未执行完成,如果提前进行交互操作,会引发空指针异常。

拓展:当 Activity 执行完 onCreate() 方法后会被调用,此时可以执行与 Activity 相关的 UI 操作

onSaveInstanceState()

保存当前Fragment的状态。该方法会自动保存Fragment的状态,比如EditText键入的文本,即使Fragment被回收又重新创建,一样能恢复EditText之前键入的文本。


###### ----------------------------- 以下几个跟 Activity 中对应方法类似 start ----------------------------

onStart()

执行该方法时,Fragment 由不可见变为可见状态。

onResume()

执行该方法时,Fragment 处于活动状态,用户可与之交互。

onPause()

执行该方法时,Fragment 处于暂停状态,但依然可见,用户不能与之交互。

onStop()

执行该方法时,Fragment 完全不可见。

—————————– 以上几个跟 Activity 中对应方法类似 end —————————-

onDestroyView()

销毁与 Fragment 有关的视图,但未与 Activity 解除绑定,依然可以通过 onCreateView 方法重新创建视图。通常在ViewPager + Fragment 结合使用时会调用此方法。

拓展:表示销毁 Fragment 相关联的 UI 布局,清除所有跟视图相关的资源。不一定在 Activity 的 onDestroy() 方法中调用。

onDestroy()

销毁 Fragment。通常按 Back 键退出或者 Fragment 被回收时调用此方法。

拓展:销毁 Fragment 对象的时候调用,一般是调用 Activity 的 onDestroy() 的时候执行。

onDetach()

解除与 Activity 的绑定(解除 Fragment 与 Activity 的关联)。在 onDestroy 方法之后调用。

setUserVisibleHint()

设置Fragment可见或者不可见时会调用此方法。在该方法里面可以通过调用 getUserVisibleHint() 获得 Fragment 的状态是可见还是不可见的,如果可见则进行懒加载操作。

拓展:当 Fragment 与 ViewPager 结合使用时,切换 Pager 时回调方法。


### ViewPager 切换 Fragment 相关生命周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Fragment 初始化创建
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: setUserVisibleHint
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onAttach
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onCreate
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onCreateView
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onActivityCreated
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// Fragment变为不可见状态
// 屏幕锁屏、回到桌面、被Activity完全覆盖 经历一样的生命周期
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onPause
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onSaveInstanceState
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStop


// 屏幕解锁
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// 切换到其他 Fragment
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onPause
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStop
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDestroyView


// 切换回本身的 Fragment
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onCreateView
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onActivityCreated
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// 回到应用
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// Fragment 退出(注意退出不会调用onSaveInstanceState方法,因为是人为退出,没有必要再保存数据)
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onPause
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStop
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDestroyView
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDestroy
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDetach


// Fragment变为部分可见状态(打开Dialog样式的Activity)
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onPause
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onSaveInstanceState


// Fragment 由不可见变为活动状态
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// Fragment由部分可见变为活动状态
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume


// Fragment 被回收又重新创建
// 被回收执行
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onPause
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onSaveInstanceState
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStop
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDestroyView
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDestroy
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onDetach


// 重新创建执行
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onAttach
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onCreate
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onCreateView
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onActivityCreated
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onStart
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: onResume
04-02 21:24:48.240 2049-2448/? I/Fragment Lifecycle: setUserVisibleHint


// 横竖屏切换:与Fragment被回收又重新创建一样。

### **补充**

onAttach() 和 onCreate() 只在 Fragment 与 Activity 第一次关联时调用。

onDestroy() 和 onDetach() 只在 Fragment 的宿主 Activity 销毁时才会被调用。

根据前 2 点,将 Fragment 通过 addToBackStack() 只涉及 onCreateView() 和 onDestroyView() 这之间的生命周期。add() 和 replace() 不会对 Fragment 的生命周期产生影响,但 add() 方法会造成 Fragment 叠加显示。

Fragment 与 ViewPager 结合使用时的生命周期与第 3 点相似。

通过 hide() 、show() 来隐藏、显示Fragment,此时 Fragment 只改变了可见性,并不涉及生命周期的改变。

不要在 Fragment 的 onCreate() 方法中操作宿主 Activity 的 UI。因为无法保证此时宿主 Activity 的 UI 已经完全初始化,某些情况下也是可以确保宿主 Activity 已经初始化完成的。

onViewCreated()

该方法在 onCreateView() 之后会被立即执行,此时可以对 View 对象进行赋值,onCreateView 是创建的时候调用,onViewCreated 是在 onCreateView 后被触发的事件,前后关系。

Fragment 中有几个比较相似的生命周期方法 onCreate、onCreatView、onViewCreated,需要仔细区分一下。

1
2
3
4
5
6
7
8
/**
* Called immediately after onCreateView(android.view.LayoutInflater, android.view.ViewGroup,
* android.os.Bundle) has returned, but before any saved state has been restored in to the view.
* This gives subclasses a chance to initialize themselves once they know their view hierarchy has
* been completely created. The fragment's view hierarchy is not however attached to its parent at
* this point.
*/
void onViewCreated(View view, Bundle savedInstanceState)

官方的一段介绍, onCreateView 是创建的时候调用,onViewCreated 会在 onCreateView 被触发后调用,前后关系;也是 fragment 中的 onCreateView 和 onViewCreated 的区别和联系。另外 onStart 运行时间位于 onViewCreated 之后。

Fragment 相关奇葩问题整理

onCreateView()调用多次?

遇到在滑动 Fragment 的过程中不断重复调用 onCreateView 的问题,导致控件不断初始化,影响程序的整体逻辑。
可以尝试把初始化操作写在 OnCreate() 中,是为了控件只初始化一次。oncreateView 会被多次执行。

另一种方案:

private View mView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (null != mView){
        ViewGroup parent = (ViewGroup) mView.getParent();
        if (null != parent) {
            parent.removeView(mView);
        }
    }else {
        mView = inflater.inflate(R.layout.main_layout, container, false);
        // 初始化 View, findViewById();
    }
    return mView;
}

setUserVisibleHint() 不被调用?

通常情况下都是因为继承的是 PagerAdapter 而不是 FragmentPagerAdapter 造成的,FragmentPagerAdapter 内部实现了对setUserVisibleHint()方法的调用,所以需要懒加载的结构最好使用 FragmentPagerAdapter + Fragment 的结构,少用PagerAdapter。

FragmentStatePagerAdapter 和 FragmentPagerAdapter 区别?

FragmentPagerAdapter 是另一种可用的 PagerAdapter,其用法和 FragmentStatePagerAdapter 基本一致,只是在卸载不需要的 fragment 时,各自采用的处理方法不同。

FragmentStatePagerAdapter 会销毁不需要的 fragment,而 FragmentPagerAdapter 是调用 detach(Fragment) 方法来处理它,只是销毁了 fragment 的视图,而 fragment 的实例由 FragmentManager 维护,因此,FragmentPagerAdapter 创建的 fragment 永远不会被销毁。

所以当数据量大时,可以选择 FragmentStatePagerAdapter,用户界面只有少量固定的 fragment 时,可以选择 FragmentPagerAdapter。

FragmentPagerAdapter+ViewPager 的注意事项 ?

使用 FragmentPagerAdapter + ViewPager 时,切换回上一个 Fragment 页面时(已经初始化完毕),不会回调任何生命周期方法以及onHiddenChanged(),只有 setUserVisibleHint(boolean isVisibleToUser)会被回调,所以如果你想进行一些懒加载,需要在这里处理。

在给ViewPager绑定FragmentPagerAdapter时,new FragmentPagerAdapter(fragmentManager)的FragmentManager,一定要保证正确,如果ViewPager是Activity内的控件,则传递getSupportFragmentManager(),如果是Fragment的控件中,则应该传递getChildFragmentManager()。只要记住ViewPager内的Fragments是当前组件的子Fragment这个原则即可。

你不需要考虑在“内存重启”的情况下,去恢复的Fragments的问题,因为FragmentPagerAdapter已经帮我们处理啦。

未完待更新…

未完待更新…