【问题标题】:NullPointerException with fragments带有片段的 NullPointerException
【发布时间】:2012-12-24 22:12:36
【问题描述】:

我的应用程序在 MainActivity 中包含带有两个片段的 ViewPager。第一个片段包含 ListView。我从来没有看到过这个,但我在 Google Play 中看到了用户关于 RuntimeException 的报告:

java.lang.RuntimeException: Unable to resume activity {com.my.project/com.my.project.MainActivity}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.my.project/com.my.project.MainActivity}: java.lang.NullPointerException
 at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2120)
 at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2135)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1668)
 at android.app.ActivityThread.access$1500(ActivityThread.java:117)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
 at android.os.Handler.dispatchMessage(Handler.java:99)
 at android.os.Looper.loop(Looper.java:130)
 at android.app.ActivityThread.main(ActivityThread.java:3683)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:507)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:875)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:633)
 at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.my.project/com.my.project.MainActivity}: java.lang.NullPointerException
 at android.app.ActivityThread.deliverResults(ActivityThread.java:2532)
 at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2107)
 ... 12 more
Caused by: java.lang.NullPointerException
 at com.my.project.FirstFragment.load(FirstFragment.java:551)
 at com.my.project.MainActivity.onActivityResult(MainActivity.java:132)
 at android.app.Activity.dispatchActivityResult(Activity.java:3908)
 at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)
 ... 13 more

这是来自 FirstFragment.java 的代码:

public class FirstFragment extends Fragment {

    ArrayList<ListItemObject> listItems = null;
    ArrayAdapter<ListItemObject> mAdapter;

    void load() {
            mAdapter.clear(); // I get error here, that is FirstFragment.java:551
            ...
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        listItems = new ArrayList<ListItemObject>();
        mAdapter = new MyAdapter(getActivity(), listItems);
        listView.setAdapter(mAdapter);
    }

}

这就是 mAdapter 的所有出现。而且我无法理解,它如何导致 NullPointerException。我认为有时 Fragment1 会破坏和重新创建,但没有 onCreateView()。 我与一位遇到此问题的用户交谈,他说我在设置中取消选中“开发人员选项”中的“不保留活动”后,此错误就会消失。我不知道,我可以修复它吗?或者我需要说我的所有用户“取消选中此选项”?

在我的 NainActivity 中:

public class MainActivity extends FragmentActivity {

    Vector<Fragment> fragments;
    ViewPager viewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        viewPager = (ViewPager) findViewById(R.id.pager);

        fragments = new Vector<Fragment>();
        fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
        fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));

        viewPager.setOffscreenPageLimit(fragments.size());
        PagerAdapter pagerAdapter = new PagerAdapter(super.getSupportFragmentManager(), fragments);

        viewPager.setAdapter(pagerAdapter);

        viewPager.setCurrentItem(0);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode==1) {
            ((MainChat)fragments.get(0)).load();
        }
    }

    class PagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter {

        private List<Fragment> fragments;

        public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
            super(fm);
            this.fragments = fragments;
        }

        @Override
        public Fragment getItem(int position) {
            return this.fragments.get(position);
        }

        @Override
        public int getCount() {
            return this.fragments.size();
        }

    }

}

【问题讨论】:

  • 你确定那是你的MainActivity吗?该错误清楚地表明MainActivity::onActivityResult 发生了一些事情,而您显然没有...
  • 何时调用 load()?可能 mAdapter 不会通过配置更改持续存在。
  • 您当然不需要让用户更改他们的设备选项,您必须修复这个问题...这可能是 支持包,但没有更多信息我们只能猜测。
  • @K-ballo,对不起,我忘记了。我编辑我的问题并添加 onActivityResult()。
  • @JoãoMelo,加载在 onActivityResult() 中调用。但这种异常很少发生。

标签: android android-fragments nullpointerexception lifecycle


【解决方案1】:

我认为错误的原因是您的 MainActivity 被破坏,然后被 Android OS 重新创建。

这意味着片段列表将在您的 MainActivity.onCreate() 方法中再次创建,但 PagerAdapter 不会使用这些片段,而是会使用 FragmentManager 为您恢复的片段实例(例如 PagerAdapter.getItem() 将不叫)。并且由于片段列表中的 FirstFragment 实例未使用,它不会收到对 FirstFragment.onCreateView() 的调用,并且 mAdapter 实例仍将为空,那么您将调用 FirstFragment.load() 方法。

解决此问题的最简单(但不是很好)的方法是通过在 MainActivity.onCreate() 中将 null 作为 savedInstanceState 参数传递来抑制片段的恢复:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    ...
}

另一种方法是按需在 PagerAdapter 中创建片段:

@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0: return Fragment.instantiate(this, Fragment1.class.getName());
        case 1: return Fragment.instantiate(this, Fragment2.class.getName());
        default: throw new RuntimException();
    }
}

并以这种方式存储片段实例:

@Override
public Object instantiateItem(View container, int position) {
    Fragment fr = (Fragment) super.instantiateItem(container, position);
    fragments[position] = fr;
    return fr;
}

(注意:第二种方式我没有测试过,所以可能行不通)

顺便说一句,您可以在 Android 4.0 设置中选中“不保留活动”开发人员选项来测试此类问题。

【讨论】:

    【解决方案2】:

    正在发生的事情是包含片段的活动已被处置。这可能在活动未显示的任何时候发生 - 您的用户所做的是更改开发人员设置,该设置会积极破坏活动以允许您查看类似这样的编码问题。问题仍然可能发生,并且设备可用的资源越少(因为它的内存很小,或者因为很多其他的东西正在运行),Android 执行此操作的频率就越高。

    在解释了潜在问题的背景之后,让我们继续讨论 onActivityResult() 代码中的具体问题。此方法在视图生命周期中的某个点被调用,在您的片段和活动周围的上下文被重构之前,因此您不能在此方法中对您的片段进行任何有意义的工作,并确保它始终有效。而是将响应保存在类级别变量中并在 onResume() 中使用它,无论活动是否被销毁,都会调用它。

    顺便说一句,您应该将 onActivityResult() 方法移到片段中 - 并始终在您自己的代码之前调用 super.onActivityResult()

    最后请注意,Android 会随心所欲地销毁和重新创建片段(例如,当您旋转设备时,所有片段都会被销毁并重新创建)。因此,您应该确保您了解并使用 Activity 和 Fragment 生命周期,而不是像 Alex 的解决方案所建议的那样反对它们。这只会导致其他难以诊断的问题。

    【讨论】:

      猜你喜欢
      • 2012-11-09
      • 1970-01-01
      • 2014-12-05
      • 2012-11-28
      • 1970-01-01
      • 2021-07-13
      • 2020-11-02
      • 1970-01-01
      相关资源
      最近更新 更多