为什么在onCreate之前会调用onAttach方法?

60
在Fragment的生命周期中,onAttach()方法在onCreate()方法之前被调用。我无法理解这一点。为什么您要首先附加一个Fragment?

1
在 onCreate() 方法中初始化前,该片段需要附加到其父活动。 - Vikas
8
实际上,真正的问题是为什么getActivity()onActivityCreated()之前是空值,即使已经调用了接收activity作为参数的onAttach()方法。 - EpicPandaForce
@clemp6r,它们比Activities更稳定,您不需要在清单中声明它们,而且它仍然比Flow/Mortar组合更美观。我稍后会尝试研究这个问题。 - EpicPandaForce
2
@EpicPandaForce 我相信 getActivity() 在 onActivityCreated() 之前返回 null,以保护您免受使用尚未初始化的活动部分的影响。 - Simon
@EpicPandaForce,这个部分中的“_caution_”回答了你的问题。 - Droidekas
显示剩余2条评论
4个回答

44

TL;DR:

为了在Android的不同UI组件之间保持设计一致性,onCreate()方法将在所有组件中具有类似的功能。

当将容器链接到内容时,例如将窗口链接到Activity和Activity链接到Fragment,需要进行初步检查以确定容器的状态。这就解释了在片段生命周期中使用和位置的onAttach()的用途。

太短了,需要更长的:

答案就在原型代码中。

@Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

另一个例子是在Jake Wharton的ActionBarSherlock库中。

为什么你想要使用像onCreate()这样的方法,它在activityservice中具有相同的目的。

onCreate()旨在处理与特定上下文创建相关的问题。如果将onCreate()用于检查其容器的状态,则没有意义。

我能想到的第二个原因是,片段被设计为独立于活动。在初始化片段之前,onAttach()提供了一种接口来确定包含活动与片段相关的状态/类型/(对片段重要的其他细节)。

编辑:

活动存在并且具有自我维持的生命周期。

对于片段:

  1. 独立的生命周期组件(与任何其他组件相同):

    • onCreate()
    • onStart()
    • onResume()
    • onPause()
    • onStop()
    • onDestroy()
  2. 基于交互的组件:

    • onAttach()
    • onCreateView()
    • onActivityCreated()
    • onDestroyView()
    • onDetach()

来自文档:

一个片段的生命周期流程受其宿主活动影响,每个连续的活动状态决定了片段可以接收哪些回调方法。例如,当活动已经接收到其onCreate()回调时,活动中的片段最多只会接收到onActivityCreated()回调。一旦活动达到恢复状态,你就可以自由地向活动添加和删除片段。因此,只有在活动处于恢复状态时,片段的生命周期才能独立改变。然而,当活动离开恢复状态时,片段再次被活动推动通过其生命周期。
回答评论中出现的另一个问题:
注意:如果你需要在片段内部获取Context对象,可以调用getActivity()。但是,在片段未附加到活动或在其生命周期结束期间被分离时,请小心仅在片段附加到活动时调用getActivity(),否则getActivity()将返回null。
设计哲学表明,片段是为重用而设计的。片段(按设计)可以(并应该)在多个活动之间使用。

onCreate的定义是负责创建一个片段。考虑方向的情况,您的片段可能是: - 在不同的方向上使用不同的布局。 - 仅适用于纵向方向而不是横向方向。 - 仅在平板电脑和手机上使用。

所有这些情况都需要在从Android的角度(onCreate())初始化片段并填充视图之前进行检查。

还要考虑无界面片段的情况。 onAttach()为您提供了所需的接口进行初步检查。


请告诉我你正在寻找答案的问题。如果与此不同,您可以单独发布并可能得到一个好的答案。但考虑到它的联系,你是怎么偶然来到这里的呢? - Droidekas

14

onAttach()方法会将宿主Activity分配给Fragment。如果在onCreate()之后调用它,则Fragment将没有上下文(getActivity()将返回null),您无法在onCreate()方法中使用该上下文进行任何操作。

另一个适合的原因是Fragment的生命周期与Activity的生命周期类似。在Activity.onAttach()中,Activity会附加到其父窗口。同样,在Fragment.onAttach()中,片段会在任何其他初始化操作之前连接到其父活动。


9
以下是关于保留片段的内容。根据Fragment setRetainInstance(boolean retain)文档:
如果设置了,当Activity重新创建时,片段的生命周期将略有不同:
  • onDestroy()将不会被调用(但onDetach()仍然会被调用,因为片段正在从其当前Activity中分离)。
  • onCreate(Bundle)不会被调用,因为片段不会被重新创建。
  • onAttach(Activity)和onActivityCreated(Bundle)仍将被调用。
请查看源代码(android.support.v4.app.FragmentManager, v21):
void moveToState(Fragment f, 
                 int newState, 
                 int transit, 
                 int transitionStyle,
                 boolean keepActive) {
    ...
    f.onAttach(mActivity);
    if (!f.mCalled) {
        throw new SuperNotCalledException("Fragment " + f
                + " did not call through to super.onAttach()");
    }
    if (f.mParentFragment == null) {
        mActivity.onAttachFragment(f);
    }

    if (!f.mRetaining) {
        f.performCreate(f.mSavedFragmentState); // <- Here onCreate() will be called
    }
    ...
}

示例

情况1:未保存碎片setRetainInstanceState(false)

应用启动。使用FragmentManager动态添加Fragment,或通过setContentView()从XML中加载Fragment。

onAttach()在Activity super.onCreate()调用之后被调用——Activity已经初始化。

MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity@1be4f2dd
MainFragment﹕ onCreate() getActivity=com.example.MainActivity@1be4f2dd

配置已更改。 Activity 从保存的状态重新创建片段,片段在 Activity 的super.onCreate() 调用内添加/附加:

MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity@2443d905
MainFragment﹕ onCreate() getActivity=com.example.MainActivity@2443d905
MainActivity﹕ super.onCreate() returned

案例 2: setRetainsInstanceState(true)

应用程序已启动。使用FragmentManager动态添加片段或通过setContentView()从XML填充。与上述相同:

onAttach()在Activitysuper.onCreate()调用后调用- Activity已经初始化。

MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity@3d54a168
MainFragment﹕ onCreate() getActivity=com.example.MainActivity@3d54a168

配置已更改。

片段onCreate()未调用,但onAttach()仍然被调用 - 你需要知道,托管活动已更改。但是片段已经创建,因此不会调用onCreate()

MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity@d7b283e
MainActivity﹕ super.onCreate() returned

2
Android开发者的文章《 Site 》中提到了为什么在Fragment生命周期时onAttach()onCreate()之前被调用的两个原因。
  1. 一个片段必须始终嵌入在一个活动中。这意味着为了让片段存在,必须有一个“活着的”活动。
    此外,当您将片段作为活动布局的一部分添加时,它将驻留在活动视图层次结构内的ViewGroup中。

所以,片段必须首先“附加”到活动中来定义自己的视图布局。

  1. onCreate()被调用用于片段的初始创建。

很明显,只有在创建前提条件(即一个片段必须始终嵌入在活动中,并且必须连接到其活动)满足时才会进行创建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接