Android app crashing after a while using Fragments and ViewPager

匿名 (未验证) 提交于 2019-12-03 02:52:02

问题:

I've got a problem with my android app crashing when trying to restore my fragments. I have not added any custom variables to the bundle that I'm trying to restore, It's all default. I'm using Fragments and ViewPager. See my code snippets below:

public static class MyAdapter extends FragmentStatePagerAdapter {     public MyAdapter(FragmentManager fm) {         super(fm);     }      @Override     public int getCount() {         return NUM_ITEMS;     }      public int getCurrentItemPosition(Fragment fragment){         return getItemPosition(fragment);     }      @Override     public Fragment getItem(int position) {         return ContentFragment.newInstance(position);     } }  public class MyActivity extends FragmentActivity {  static final int NUM_ITEMS = 100000; public int currentSelectedPage; private int changeToFragmentIndex; public DateTime midDate; MyAdapter mAdapter; ViewPager mPager;  /** Called when the activity is first created. */ @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.diary);     MyApplication application = (MyApplication)getApplication();     application.dataController.myActivity = this;     mAdapter = new MyAdapter(getSupportFragmentManager());     mPager = (ViewPager)findViewById(R.id.pager);     mPager.setAdapter(mAdapter);     int newDay = application.daysBetween(DateTime.now(), DateTime.parse(application.day.dateToShortString()));      this.currentSelectedPage = NUM_ITEMS/2+newDay;     mPager.setCurrentItem(NUM_ITEMS/2+newDay);     mPager.setOnPageChangeListener(new SimpleOnPageChangeListener(){         public void onPageSelected(int position){             currentSelectedPage = position;             ContentFragment fragment = (ContentFragment) mAdapter.instantiateItem(mPager, currentSelectedPage);             fragment.loadData();         }     }); }  }  public class ContentFragment extends Fragment { private View v; static final int NUM_ITEMS = 100000;  static ContentFragment newInstance(int num) {     ContentFragment f = new ContentFragment();      return f; }  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,         Bundle savedInstanceState) {     if (container == null)         return null;     v = inflater.inflate(R.layout.diarycontent, container, false);     return v; }  @Override public void onActivityCreated(Bundle savedInstanceState) {     super.onActivityCreated(savedInstanceState);  }  @Override public void onSaveInstanceState(Bundle savedInstanceState){     super.onSaveInstanceState(savedInstanceState);     setUserVisibleHint(true); } } 

I receive this stackstrace:

Caused by: java.lang.NullPointerException at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:519) at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:1 55) at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:522) 

As I have understood this may be a known problem with the support package and that one possible solution would be to use setUserVisibleHint(true) in onSaveInstanceState. But that didn't help.

Does anyone know another solution to the problem or what I've done wrong?

回答1:

I have faced same type of issue once I started using ViewPager with FragmentStatePagerAdapter inside Tab.

Then I played with all forums related to this issue and break my head for two days to find out why this issue is occurred and how to resolve it but does not found any perfect solution that fulfills as per my requirement.

I got the solution as in order to avoid the NPE crash use FragmentPagerAdapter instead of FragmentStatePagerAdapter. When I replace FragmentPagerAdapter instead of FragmentStatePagerAdapter then not faced NPE crash issue but the same page is not refreshed itself for further navigation(which is my requirement).

Finally override the saveState method on FragmentStatePagerAdapter and everything working fine as expected.

@Override public Parcelable saveState() {     // Do Nothing     return null; } 


How I reproduce this issue easily :
I am using the higher version(4.1) device and followed the below steps:
1. Go to Settings -> Developer Options.
2. Click the option "Do not keep activities".

Now I played with my app.

Before override the saveState() method each time the app is crashing due to the NPE at android.support.v4.app.FragmentManagerImpl.getFragment(Unknown Source). But after override saveState() no crash is registered.

I hope by doing so you will not having any issue.



回答2:

I initially used solution suggested by @Lalit Kumar Sahoo. However, I noticed a serious issue: every time I rotated the device, the Fragment Manager added new fragments without removing the old ones. So I searched further and I found a bug report with a workaround suggestion (post #1). I used this fix and tested with my app, the issue appears to be resolved without any side-effects:

Workaround: Create custom FragmentStatePagerAdapter in project's src/android/support/v4/app folder and use it.

package android.support.v4.app;  import android.os.Bundle; import android.view.ViewGroup;  public abstract class FixedFragmentStatePagerAdapter extends FragmentStatePagerAdapter {    public FixedFragmentStatePagerAdapter(FragmentManager fm) {     super(fm);   }    @Override   public Object instantiateItem(ViewGroup container, int position) {     Fragment f = (Fragment)super.instantiateItem(container, position);     Bundle savedFragmentState = f.mSavedFragmentState;     if (savedFragmentState != null) {       savedFragmentState.setClassLoader(f.getClass().getClassLoader());     }     return f;   } } 


回答3:

Why exactly are you doing:

mPager.setOnPageChangeListener(new SimpleOnPageChangeListener(){     public void onPageSelected(int position){         currentSelectedPage = position;         ContentFragment fragment = (ContentFragment) mAdapter.instantiateItem(mPager, currentSelectedPage);         fragment.loadData();     } }); 

This:

@Override public Fragment getItem(int position) {     return ContentFragment.newInstance(position); } 

Instantiates the fragment.

You can load data like this in the fragment :

@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); loadData(); } 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!