16

My activity uses TabLayout + ViewPager.

The number of tabs and pages here are dynamic depending on the data fetch from the server.

The crash are reported via Crashlytics, I'm not able to replicate it.

My Activity code:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        boolean isAppRestarting = PrefUtils.getBoolean("app_restarting", false);
        if (isAppRestarting) {
            super.onCreate(null);
            this.savedInstanceState = null;
        } else {
            super.onCreate(savedInstanceState);
            this.savedInstanceState = savedInstanceState;
        }

        initSlidingTabs();

        fetchSomeData(); // see onDataFetched() below
    }

private void initSlidingTabs() {
        List<FragmentWithTitle> sdsTabs = new ArrayList<>();
            Logger.i("There is saved instance state");
            ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES);
            if (titles != null) {
                for (int i = 0; i < titles.size(); i++) {
                    if (getFragment(i) != null) {
                        sdsTabs.add(new FragmentWithTitle(getFragment(i), titles.get(i)));
                    }
                }
            }

        mTabsAdapter = new MyTabsAdapter(getSupportFragmentManager(), sdsTabs);

        mViewPager = (ViewPager) findViewById(R.id.layoutViewPagerSlidingTabs);
        mViewPager.setAdapter(mTabsAdapter);
        mViewPager.setOffscreenPageLimit(2);

        mSlidingTabs = (TabLayout) findViewById(R.id.tabs);
        mSlidingTabs.setupWithViewPager(mViewPager);
}

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mTabsAdapter != null) {
            outState.putStringArrayList(TAB_TITLES, mTabsAdapter.getTitles());
        }
    }

    private Fragment getFragment(int position) {
        return savedInstanceState == null ? mTabsAdapter.getItem(position) : getSupportFragmentManager()
                .findFragmentByTag(getFragmentTag(position));
    }

    private String getFragmentTag(int position) {
        return "android:switcher:" + R.id.layoutViewPagerSlidingTabs + ":" + position;
    }

/**
Callback of fetchData() in onCreate()
*/
private void onDataFetched(List<AbcObject> abcObjects) {
        if (savedInstanceState == null) {
            if (abcObjects != null) {
                for (AbcObject abcObject : abcObjects) {
                    mTabsAdapter.addTab(new FragmentWithTitle(AbcFragment.newInstance(), abcObject.getTitle()));
                }
            }
        } else {
            Logger.d("There is saved instance state");
            ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES);
            if (titles != null) {
                for (int i = 0; i < titles.size(); i++) {
                    mTabsAdapter.addTab(new FragmentWithTitle(getFragment(i), titles.get(i)));
                }
            }
        }

        mTabsAdapter.notifyDataSetChanged();
    }

MyTabsAdapter:

public class MyTabsAdapter extends FragmentPagerAdapter {

    private List<FragmentWithTitle> mTabs = new ArrayList<>();

    public MyTabsAdapter(FragmentManager fm, List<FragmentWithTitle> sdsTabs) {
        super(fm);
        this.mTabs = sdsTabs;
    }

    @Override
    public Fragment getItem(int position) {
        return mTabs.get(position).getFragment();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTabs.get(position).getTitle();
    }

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

    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    /**
     * Prevent java.lang.IllegalStateException Fragment no longer exists for key android:target_state: index 5
     *
     * @return
     */
    @Override
    public Parcelable saveState() {
        return null;
    }

    public ArrayList<String> getTitles() {
        ArrayList<String> titles = new ArrayList<>();
        for (int i = 0; i < mTabs.size(); i++) {
            titles.add((String) mTabs.get(i).getTitle());
        }
        return titles;
    }

    public void addTab(FragmentWithTitle fragmentWithTitle) {
        mTabs.add(fragmentWithTitle);
    }
}

Full stacktrace:

Fatal Exception: java.lang.IllegalStateException: Can't change tag of fragment ShopFrag{4dbb6e90 #0 id=0x7f1200dd android:switcher:2131886301:0}: was android:switcher:2131886301:0 now android:switcher:2131886301:9
       at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:444)
       at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:426)
       at android.support.v4.app.FragmentPagerAdapter.instantiateItem(FragmentPagerAdapter.java:103)
       at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1038)
       at android.support.v4.view.ViewPager.populate(ViewPager.java:1252)
       at android.support.v4.view.ViewPager.populate(ViewPager.java:1120)
       at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1646)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
       at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
       at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
       at android.view.View.measure(View.java:17396)
       at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1081)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
       at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
       at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
       at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
       at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
       at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
       at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
       at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2505)
       at android.view.View.measure(View.java:17396)
       at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2175)
       at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1316)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1513)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1200)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6401)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
       at android.view.Choreographer.doCallbacks(Choreographer.java:603)
       at android.view.Choreographer.doFrame(Choreographer.java:573)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
       at android.os.Handler.handleCallback(Handler.java:733)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:157)
       at android.app.ActivityThread.main(ActivityThread.java:5335)
       at java.lang.reflect.Method.invokeNative(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:515)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
       at dalvik.system.NativeStart.main(NativeStart.java)

FragmentWithTitle:

public class FragmentWithTitle {
    public FragmentWithTitle(Fragment mFragment, CharSequence mTitle) {
        this.fragment = mFragment;
        this.title = mTitle;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public CharSequence getTitle() {
        return title;
    }

    public void setTitle(CharSequence title) {
        this.title = title;
    }

    private Fragment fragment;
    private CharSequence title;
}
ericn
  • 12,476
  • 16
  • 84
  • 127
  • 2
    Sounds a lot like: [Can't change tag of fragment Error - Trying to use a PagerAdapter for switching](http://stackoverflow.com/questions/24355838/cant-change-tag-of-fragment-error-trying-to-use-a-pageradapter-for-switching). – Tigger Feb 05 '17 at 03:05
  • I know, read that but I cannot apply anything from the answer there @Tigger – ericn Feb 05 '17 at 03:24
  • Please add your class `FragmentWithTitle` – Haris Qurashi Feb 08 '17 at 13:21
  • @eric read this article...You might come up with some workaround https://medium.com/@ali.muzaffar/looping-infinite-viewpager-with-page-indicator-in-android-ce741f25702a#.ipllsqvh6 – PN10 Feb 08 '17 at 14:20
  • thanks @PN10 but it has nothing to do with my problem – ericn Feb 10 '17 at 02:45
  • Can the person who downvoted my question come forward with a comment please? – ericn Feb 10 '17 at 02:46

1 Answers1

16

The FragmentPagerAdapter already caches the Fragments for you. Each fragment is assigned a tag, and then the FragmentPagerAdapter tries to call findFragmentByTag. It only calls getItem() if the result from findFragmentByTag is null.

You're probably getting this error because you're adding the same fragment instance to the list. You should create a new instance for each page.

Example form document :

//...
      public static class MyAdapter extends FragmentPagerAdapter {
            public MyAdapter(FragmentManager fm) {
                super(fm);
            }

            @Override
            public int getCount() {
                return NUM_ITEMS;
            }

            @Override
            public Fragment getItem(int position) {
                return ArrayListFragment.newInstance(position);// IMPORTANT
            }
        }
    //..

Refer :FragmentPagerAdapter

Main thread : Retrieve a Fragment from a ViewPager

ericn
  • 12,476
  • 16
  • 84
  • 127
Charuක
  • 12,953
  • 5
  • 50
  • 88
  • Thanks @Charuක, is there any drawback in terms of memory management? – ericn Feb 10 '17 at 03:13
  • 1
    @eric you mean by returning new instance i guess no, it gets called only findFragmentByTag is null http://stackoverflow.com/questions/19339500/when-is-fragmentpageradapters-getitem-called http://stackoverflow.com/questions/12581896/fragmentpageradapter-getitem-is-not-called – Charuක Feb 10 '17 at 03:58
  • Please check the size of fragment name_array and adapter size. It would be resolve the issue. – vinay Mar 19 '20 at 11:35