一个fragment的onAttach方法可以在Activity的onCreate方法结束前被调用吗?

4

我遇到了一个非常罕见的崩溃问题(每几千个会话中才会出现一次),我正在试图追踪这个问题。我有一个Activity,在其onCreate方法中创建了一些Fragment,但没有显示或附加它们:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.activity_main);

    mainMenuFragment = new MainMenuFragment();
    locationFragment = new LocationFragment();

    mainPresenter = new MainPresenter(this);
}

在这段代码中,我还创建了一个"MainPresenter",它来自包含我们所有业务逻辑的库。该Presenter在片段的onAttach方法中被使用:
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    MainActivity mainActivity = (MainActivity) activity;
    mainPresenter = mainActivity.getMainPresenter();
    mainPresenter.refreshUI();
}

问题是,我偶尔会在 onAttach 中遇到空指针异常。有可能在某些罕见情况下,片段的 onAttach 执行早于 Activity 的 onCreate 完成(即 mainPresenter 为空)吗?
更新:
以下是崩溃前调用堆栈的部分内容,希望对您有所帮助:
android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3253)
android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3349)
android.app.ActivityThread.handleRelaunchActivity (ActivityThread.java:5383)
android.app.ActivityThread.access$1200 (ActivityThread.java:221)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:1800)
android.os.Handler.dispatchMessage (Handler.java:102)
android.os.Looper.loop (Looper.java:158)
android.app.ActivityThread.main (ActivityThread.java:7225)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1230)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1120)

我这周遇到了这个确切的问题。我发现,如果我在Android Studio的Android Monitor选项卡中将应用程序终止后重新启动它,在此期间出现这个问题的概率更高。如果你需要一个可靠的复现来进行测试,这可能会有帮助。 - stkent
@stkent - 谢谢,我会尝试一下。 - user3690202
2个回答

3
是的。请参阅https://developer.android.com/reference/android/app/Fragment.html#onCreate(android.os.Bundle)中的文档。片段.onAttach发生在片段.onCreate之前,如果onCreate的文档说在此生命周期方法期间活动仍然可以处于创建过程中,那么这意味着它绝对可以在onAttach中正在建设中。
至于您的特定场景,如果那是您的整个活动的onCreate,则那些片段根本没有连接到应用程序;它们只被实例化,不能有Android生命周期调用。我假设有更复杂的事情正在进行,当配置更改或其他原因导致片段重新添加时,您会遇到崩溃。

嗨,是的,那就是我整个onCreate的内容,你已经抓住了问题的关键点 - 我看过你提供的文档,但问题在于这些片段尚未添加到片段管理器中,因此正如你所说,似乎发生了更复杂的事情,我正在寻找答案以了解可能发生了什么。 - user3690202
我已经更新了问题,并附上了部分调用堆栈,以便更好地理解这个奇怪的问题。 - user3690202
由于handleRelaunchActivity在该堆栈中,很可能是在进行配置更改期间发生的。在活动被创建之前,片段管理器会重新连接在配置更改之前附加的片段。您可以在onActivityCreated()中调用getActivity()并将其转换为MainActivity。 - Chip Thien
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user3690202
问题相当简单 - 业务逻辑库被设计为只能创建一次。正如您所看到的,我正在Activity onCreate中创建它。然而,有时在进程运行期间重新创建Activity,导致创建额外的Presenter。这会引起问题。这不是一些简单的应用程序,可以将其所有状态存储在Bundle中。理想的解决方案是防止片段管理器重新创建片段。 - user3690202
显示剩余2条评论

0
您可能想将Activity#onCreate声明更改为:
@Override
protected void onCreate(Bundle savedInstanceState) {
    mainMenuFragment = new MainMenuFragment();

    locationFragment = new LocationFragment();

    mainPresenter = new MainPresenter(this);

    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

   setContentView(R.layout.activity_main);
} 

这是因为 super.onCreate(savedInstanceState) 会触发活动片段的(重新)附加,系统通过调用 Fragment.onAttach() 来完成此操作。
然而,在您的情况下,mainPresenter 应该已经被实例化/初始化,因为它在 Fragment.onAttach 中需要使用,但它还没有被初始化,因为它位于当前正在执行的 super.onCreate(savedInstanceState) 下面。

自从我发了这个问题以来,我学到了更多关于活动和片段的知识。我认为你的答案是错误的,尽管我很感激。Google故意说要小心不要混淆片段生命周期和活动生命周期。事实上,添加到片段管理器中的片段可以在活动onCreate被调用之前创建和附加。 - user3690202
事实是,片段可以在活动 onCreate 被调用之前创建并附加到片段管理器中。我猜你只是用自己的话重复了我的答案,因为这正是我在我的答案中传达的。 - Akintunde
我猜你的回答基本上是用不太清晰的方式重新表述已经被广泛接受的答案。 - user3690202

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