NullPointerException с фрагментами

Мое приложение содержит ViewPager в MainActivity с двумя фрагментами. Первый фрагмент содержит ListView. Я такого никогда не видел, но вижу отчеты пользователей о RuntimeException в Google Play:

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();
        }

    }

}

person BArtWell    schedule 24.12.2012    source источник
comment
Вы уверены, что это ваш MainActivity? В ошибке четко указано, что на MainActivity::onActivityResult происходит что-то, чего у вас, по-видимому, нет...   -  person K-ballo    schedule 25.12.2012
comment
Когда вызывается load()? Вероятно, mAdapter не сохраняется из-за изменений конфигурации.   -  person João Melo    schedule 25.12.2012
comment
Вам, конечно же, не нужно, чтобы пользователь менял параметры своего устройства, вы должны исправить эту проблему... Вероятно, это результат известной проблемы в Пакете поддержки, но без дополнительной информации мы можем только догадываться.   -  person K-ballo    schedule 25.12.2012
comment
@K-ballo, извини, забыл. Я редактирую свой вопрос и добавляю onActivityResult().   -  person BArtWell    schedule 25.12.2012
comment
@JoãoMelo, загрузка вызывается в onActivityResult(). Но это исключение происходит нечасто.   -  person BArtWell    schedule 25.12.2012
comment
Опубликуйте строку 551 вашего класса FirstFragment и строку 132 вашего MainActivity..LogCat сообщает вам, что эти строки являются вашей проблемой. com.my.project.MainActivity.onActivityResult(MainActivity.java:132)   -  person Jade Byfield    schedule 25.12.2012
comment
@JadeByfield, 551: mAdapter.clear(); и 132: ((MainChat)fragments.get(0)).load();   -  person BArtWell    schedule 25.12.2012


Ответы (2)


Я думаю, что причина ошибки в том, что ваша MainActivity была уничтожена, а затем воссоздана ОС Android.

Это означает, что список фрагментов будет снова создан в вашем методе MainActivity.onCreate(), но PagerAdapter не будет использовать эти фрагменты, вместо этого он будет использовать экземпляры фрагментов, которые были восстановлены для вас FragmentManager (например, PagerAdapter.getItem() не будет вызываться ). И поскольку экземпляр FirstFragment из вашего списка фрагментов не используется, он не получит вызов FirstFragment.onCreateView(), а экземпляр mAdapter по-прежнему будет нулевым, тогда вы вызовете метод FirstFragment.load().

Самый простой (но не очень хороший) способ решить эту проблему — подавить восстановление ваших фрагментов, передав null в качестве параметра saveInstanceState в MainActivity.onCreate():

@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, чтобы проверить такие проблемы.

person Alex Vasilkov    schedule 25.12.2012

Происходит то, что активность, содержащая фрагменты, была удалена. Это может произойти в любое время, когда действие не отображается — ваш пользователь изменил настройку разработчика, которая агрессивно уничтожает действия, чтобы вы могли видеть проблемы с кодированием, подобные этой. Проблема все еще может возникнуть, и чем меньше ресурсов доступно устройству (либо из-за того, что у него мало памяти, либо из-за того, что запущено много других вещей), тем чаще Android будет это делать.

Объяснив предысторию основной проблемы, давайте перейдем к конкретной проблеме в вашем onActivityResult() коде. Этот метод вызывается в какой-то момент жизненного цикла представления до того, как будет воссоздан контекст вокруг ваших фрагментов и действий, поэтому вы не можете выполнять какую-либо значимую работу с вашими фрагментами в этом методе и быть уверенным, что он всегда будет работать. Вместо этого сохраните ответ в переменной уровня класса и используйте его в onResume(), который будет вызываться независимо от того, было ли действие уничтожено или нет.

Кроме того, вы должны переместить метод onActivityResult() во фрагмент и всегда вызывать super.onActivityResult() перед вашим собственным кодом.

Наконец, обратите внимание, что Android уничтожает и воссоздает фрагменты по своей прихоти (например, когда вы поворачиваете свое устройство, все ваши фрагменты уничтожаются и создаются заново). Поэтому вы должны убедиться, что понимаете и работаете с жизненными циклами Activity и Fragment, а не против них, как предлагает решение Alex. Это приведет только к другим трудно диагностируемым проблемам.

person Phil Haigh    schedule 28.04.2013