且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

活动在后台被杀死后应用程序崩溃

更新时间:2022-12-27 10:55:50

经过一番研究,似乎问题出在 FragmentPagerAdapter 的方法命名错误 - 被命名为 getItem(),但没有明确指定抽象方法 getItem(int position) 应该返回一个片段的新实例 而不仅仅是获取一个实例".

After doing some research, it seems that the problem stems from the misnaming of FragmentPagerAdapter's method - being named getItem(), but not clearly specifying that the abstract method getItem(int position) is supposed to return a new instance of a fragment rather than just "get an instance of one".

当然,对于一个错误的名称已经存在 7 年,我们无能为力,但至少我们可以在您的代码中修复由该问题引起的错误;)

Of course, there is not much we can do about an incorrect name after it's been out in the wild for 7 years, but at least we can fix the bug that stems from this issue in your code ;)

不用多说,你的 NPE 的原因是 onCreateView(你的 Presenter 被实例化的地方)从未被调用.

Without further ado, the cause of your NPE is that onCreateView (where your Presenter is instantiated) is never called.

发生这种情况是因为您正在此处创建片段:

This happens because you are creating the fragment here:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)
    ...
    homeFragment = HomeScreenFragment.newInstance()
    incidentFragment = IncidentScreenFragment.newInstance()
}

您从 FragmentPagerAdapter 中的 getItem(int position) 内部返回此片段:

You return this fragment from inside getItem(int position) in your FragmentPagerAdapter:

override fun getItem(position: Int): Fragment = when(position) {
     ...
     1 -> activity.incidentFragment
     ...
}

所以我们对 activity.incidentFragment 的了解是,onCreateView() 永远不会被调用.

So what we know about activity.incidentFragment is that in it, onCreateView() is never called.

这是因为它从未真正添加到 FragmentManager 并且从未显示在屏幕上.

This is caused by the fact that it's never actually added to a FragmentManager and never displayed on the screen.

那是因为活动中的 super.onCreate(savedInstanceState) 使用它们的无参数构造函数,通过反射重新创建所有 Fragment,同时保留它们的标签(参见 findFragmentByTag).

That's because super.onCreate(savedInstanceState) in Activity recreates all Fragments, using their no-args constructor, via reflection, while keeping their tag (see findFragmentByTag).

正如你在这个答案中看到的,或者我可以在这里引用:

So as you can see in this answer, or as I can quote here:

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));

getItem(position) 方法仅在 FragmentPagerAdapter 为片段设置的片段标记未找到 Fragment 时调用,该片段在低内存条件杀死您的应用程序后会自动重新创建.

The getItem(position) method is only called if the Fragment is not found by the fragment tag that the FragmentPagerAdapter sets for the fragment, which IS automatically recreated after low memory condition kills your app.

因此,您的新片段(您在 Activity 中手动创建的)从未使用过,因此它没有视图,从未初始化,从未添加到 FragmentManager,它与 ViewPager 中的实际内容不同,并且当你调用它时它会崩溃.轰!

Therefore, YOUR new fragment (that you create by hand in the Activity) is NEVER used, and therefore it has no view, never initialized, never added to FragmentManager, it's not the same instance as what's actually inside your ViewPager, and it crashes when you call it. Boom!

解决方案是在 FragmentPagerAdapter 的 getItem(position) 方法中实例化 Fragment. 要获取 Fragment 的实例,请使用 这个答案.

Solution is to instantiate the Fragment inside FragmentPagerAdapter's getItem(position) method. To get an instance of the fragment, use this answer.