【问题标题】:NPE in Fragment.setUserVisibleHint() when using ViewPager使用 ViewPager 时 Fragment.setUserVisibleHint() 中的 NPE
【发布时间】:2015-10-02 15:17:20
【问题描述】:

我对此感到茫然。我在 ViewPager 中手动切换标签。我的 Activity 中有这段代码:

@Override
public void onBackPressed()
{
    if (childFragmentStack.empty())
    {
        // Go to the devices screen
        Intent intent = new Intent(this, SelectDeviceActivity.class);
        startActivity(intent);
    }
    else
    {
        Fragment fragment = childFragmentStack.pop();

        if (fragment == null)
        {
            return;
        }

        processingBackStack = true;

        if (fragment instanceof ViewChildFragment)
        {
            viewFragment.activateFragment((ViewChildFragment) fragment);
            mViewPager.setCurrentItem(VIEW_FRAGMENT_INDEX, true);
        }
        else if (fragment instanceof SetupChildFragment)
        {
            setupFragment.activateFragment((SetupChildFragment) fragment);
            mViewPager.setCurrentItem(SETUP_FRAGMENT_INDEX, true); //**
        }
        else if (fragment == homeFragment)
        {
            mViewPager.setCurrentItem(HOME_FRAGMENT_INDEX, true); //**
        }

        processingBackStack = false;
    }
}

如果我在标签之间滚动,我会将它们添加到堆栈('childFragmentStack')中。我正在使用 FragmentPagerAdapter 来处理片段。发生的情况是,如果我执行 View->Setup->View->Setup 之类的操作,然后将其反转,它只会到达 Setup->View->CRASH。就像当我按下 Back 时,Setup Fragment 对于我正在做的事情不再有效,但它永远不会重新创建! Setup 片段仅在 MainActivity.onCreate() 中创建,因此它应该仍然存在且有效。

NPE 发生在我标记为 ** 的行上。这是完整的堆栈跟踪:

    04-18 16:04:57.096: E/AndroidRuntime(13072): FATAL EXCEPTION: main
    04-18 16:04:57.096: E/AndroidRuntime(13072): java.lang.NullPointerException
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.app.Fragment.setUserVisibleHint(Fragment.java:841)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.app.FragmentPagerAdapter.setPrimaryItem(FragmentPagerAdapter.java:130)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.view.ViewPager.populate(ViewPager.java:1066)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:550)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:509)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:501)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at com.lochinvar.serf.MainActivity.onBackPressed(MainActivity.java:234)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.app.Activity.onKeyUp(Activity.java:2131)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.app.Activity.dispatchKeyEvent(Activity.java:2361)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1819)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3577)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.view.ViewRootImpl.handleImeFinishedEvent(ViewRootImpl.java:3547)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2797)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.os.Handler.dispatchMessage(Handler.java:99)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.os.Looper.loop(Looper.java:137)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at android.app.ActivityThread.main(ActivityThread.java:4745)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at java.lang.reflect.Method.invokeNative(Native Method)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at java.lang.reflect.Method.invoke(Method.java:511)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    04-18 16:04:57.096: E/AndroidRuntime(13072):    at dalvik.system.NativeStart.main(Native Method)

[编辑] 我忘了提到我覆盖了 FragmentPagerAdapter.getPageTitle() 并且它从不返回 null (默认情况下转到字符串)。

【问题讨论】:

  • mViewPager 好像是空的,有什么地方可以设置成空的吗?
  • 堆栈跟踪在 Fragment.setUserVisibleHint() 中有异常,所以我认为不是因为 mViewPager 为空。不,它不是 null - 它只在 onCreate() 中分配,在其他任何地方都没有。
  • 发布您的适配器代码,以及activateFragment 方法的作用。

标签: android android-fragments


【解决方案1】:

终于!我现在能够可靠地重现此错误!

我发现重新创建错误的另一种方法是关闭活动/应用程序,然后快速使用 ViewPager 片段重新打开页面。您可能需要尝试几次,因为在我的测试中,我必须在大约 30 毫秒内重新打开应用程序。对于不同速度的设备,此时间可能会更慢或更快。

问题是我只显式地创建了一次片段(使用new),并保留了对该实例的引用,以便我可以重用它。解决此问题的一个简单方法是始终返回 Fragment 的 new 实例 FragmentPagerAdapter.getItem(...),如下所示。

public class ViewPagerAdapter extends FragmentPagerAdapter {
    ...

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0: return mMyFragment; // Error. Has the edge-case crash.
            case 1: return new MyFragment(); // Works.
            default: return new MyDefaultFragment();
        }
    }
}

ps - 根本问题可能与 Fragment 生命周期有关,并在它被销毁时尝试再次使用它。

pps - 此解决方案修复了 android.app.Fragment.setUserVisibleHint(Fragment.java:997) 的 NullPointerException,并且也适用于 android.support.v4.app.Fragment.setUserVisibleHint

【讨论】:

    【解决方案2】:

    这对我来说是一个真正的脑筋急转弯。我们删除了所有替换Fragments 的代码,并在Activity 的整个生命周期中保留了相同的片段,但仍然存在这个问题。直到我们viewPager.setOffscreenPageLimit(TABS);,其中TABS 是标签的数量(在我们的例子中为4),我们才停止获取引用的NullPointerException

    FWIW -- 我很确定问题出在 Google 的代码中。我们无法在运行 Lollipop 的 Nexus 5 上失败,但在运行 Kitkat 的三星设备上失败。当我跟踪错误本身时,看起来失败是因为被引用的Fragment 已经通过了内部Fragment.initState 函数,因为Fragmentid 是-1。

    【讨论】:

      【解决方案3】:

      即使我使用了 FragmentStatePagerAdapter,我也遇到了非常相似的情况(Fragment.setUserVisibleHint 中的相同异常)。当底层数据发生变化时,我的应用程序发生随机(因此难以重现)崩溃。

      就我而言,我终于能够通过覆盖适配器中的以下方法来摆脱这种崩溃:

      @Override
      public void restoreState(Parcelable state, ClassLoader loader) {
          // don't super !
      }
      

      考虑到 StackOverflow 中与此类似的问题的数量,我确实认为这门课存在错误。

      我希望这对某人有用。

      编辑:我在android.support.v4.app.Fragment.setUserVisibleHint null pointer on app resuming 上发布了类似的答案,但是我不确定这些问题是否重复。但是,我认为这个答案可以帮助一些人找到这些类似的症状。我应该如何注意这一点?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-16
        • 2011-10-30
        • 1970-01-01
        • 1970-01-01
        • 2016-08-02
        • 1970-01-01
        相关资源
        最近更新 更多