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 ви казва, че тези редове са вашият проблем Причинен от: java.lang.NullPointerException в com.my.project.FirstFragment.load(FirstFragment.java:551) в 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 OS.

Това означава, че списъкът с фрагменти ще бъде създаден отново във вашия метод MainActivity.onCreate(), но PagerAdapter няма да използва тези фрагменти, вместо това ще използва екземпляри на фрагменти, които са били възстановени за вас от FragmentManager (напр. PagerAdapter.getItem() няма да бъде извикан ). И тъй като екземплярът на FirstFragment от вашия списък с фрагменти не се използва, той няма да получи извикване към FirstFragment.onCreateView() и екземплярът на mAdapter все още ще бъде нулев, тогава ще извикате метода FirstFragment.load().

Най-лесният (но не много добър) начин да коригирате този проблем е да потиснете възстановяването на вашите фрагменти, като подадете null като параметър savedInstanceState в 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 унищожава и пресъздава фрагменти по собствена прищявка (например, когато завъртите устройството си, всичките ви фрагменти се унищожават и създават отново). Следователно трябва да се уверите, че разбирате и работите с жизнените цикли на активността и фрагмента, а не срещу тях, както предполага решението на Алекс. Това само ще доведе до други трудни за диагностициране проблеми.

person Phil Haigh    schedule 28.04.2013