scott13
scott13

Reputation: 77

TransactionTooLargeException when using FragmentStatePagerAdapter and launching a new activity

This is actually a problem that stemmed from somewhere else, but I have narrowed it down to the following scenario. In my MainActivity, I use a ViewPager to have three tabs on the bottom to swap through my three Fragments. If I click a button to move to a new Activity after moving to any Fragment, I get the following error:

FATAL EXCEPTION: main
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 861520 bytes
  at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3752)
  at android.os.Handler.handleCallback(Handler.java:751)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:154)
  at android.app.ActivityThread.main(ActivityThread.java:6077)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: android.os.TransactionTooLargeException: data parcel size 861520 bytes
  at android.os.BinderProxy.transactNative(Native Method)
  at android.os.BinderProxy.transact(Binder.java:615)
  at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3606)
  at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3744)
  at android.os.Handler.handleCallback(Handler.java:751) 
  at android.os.Handler.dispatchMessage(Handler.java:95) 
  at android.os.Looper.loop(Looper.java:154) 
  at android.app.ActivityThread.main(ActivityThread.java:6077) 
  at java.lang.reflect.Method.invoke(Native Method) 
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755) 

However, if I click the button before moving between the different fragments in the ViewPager, I do not get a crash. I have explored all possibilities in making my fragments simply completely empty to see if the problem lies there, but it appears to be linked to the MainActivity or the FragmentStatePagerAdapater.

Here is the code for them both:

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import com.mypackage.Fragments.GroupsFragment;
import com.mypackage.Fragments.HomeFragment;
import com.mypackage.Fragments.ProfileFragment;
import com.mypackage.R;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the ViewPager and set it's PagerAdapter so that it can display items
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        viewPager.setAdapter(new MainFragmentPagerAdapter(getSupportFragmentManager()));

        // Give the TabLayout the ViewPager
        TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
        tabLayout.setupWithViewPager(viewPager);

        Button mTestButton = (Button) findViewById(R.id.test);

        mTestButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                moveToGroupActivity();
            }
        });
    }

    public void moveToGroupActivity() {
        Intent myIntent = new Intent(this, GroupActivity.class);
        startActivity(myIntent);
    }

    public static class MainFragmentPagerAdapter extends FragmentStatePagerAdapter {

        final int PAGE_COUNT = 3;
        private String tabTitles[] = new String[] { "Home", "Groups", "Profile" };

        public MainFragmentPagerAdapter(FragmentManager fm) {
            super(fm);


 }

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

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return HomeFragment.newInstance();
            case 1:
                return GroupsFragment.newInstance();
            case 2:
                return ProfileFragment.newInstance();
            default:
                return null;
        }
    }

    @Override
    public CharSequence getPageTitle(int position) {
        // Generate title based on item position
        return tabTitles[position];
    }
}

}

To be clear: I have a button that moves to a different Activity in an Activity that consists of a ViewPager that moves between three fragments. If I click the button before moving to a different fragment, it will load just fine. If I move to a fragment and then click the button, I get the crash noted above.

I do not ever use onSaveInstanceState() or anything like that. My only guess is that the FragmentStatePagerAdapter is putting the fragments into memory and causing the crash, but I really do not know.

Upvotes: 3

Views: 2219

Answers (2)

Sushil Chaudhary
Sushil Chaudhary

Reputation: 181

This issue may be solved clearing the last saved Instance of the object. Try using in your activity class.

 @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.clear();
    }

Upvotes: 0

Mark
Mark

Reputation: 41

The TransactionTooLargeException has been plaguing us for about 4 months now, and we've finally resolved the issue!

What was happening was we are using a FragmentStatePagerAdapter in a ViewPager. The user would page through and create 100+ fragments (its a reading application).

Although we manage the fragments properly in destroyItem(), in Androids implementation of FragmentStatePagerAdapter there is a bug, where it kept a reference to the following list:

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();

And when the Android's FragmentStatePagerAdapter attempts to save the state, it will call the function

@Override
public Parcelable saveState() {
    Bundle state = null;
    if (mSavedState.size() > 0) {
        state = new Bundle();
        Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
        mSavedState.toArray(fss);
        state.putParcelableArray("states", fss);
    }
    for (int i=0; i<mFragments.size(); i++) {
        Fragment f = mFragments.get(i);
        if (f != null && f.isAdded()) {
            if (state == null) {
                state = new Bundle();
            }
            String key = "f" + i;
            mFragmentManager.putFragment(state, key, f);
        }
    }
    return state;
}

As you can see, even if you properly manage the fragments in the FragmentStatePagerAdapter subclass, the base class will still store an Fragment.SavedState for every single fragment ever created. The TransactionTooLargeException would occur when that array was dumped to a parcelableArray and the OS wouldn't like it 100+ items.

Therefore the fix for us was to override the saveState() method and not store anything for "states".

@Override
public Parcelable saveState() {
    Bundle bundle = (Bundle) super.saveState();
   bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out
    return bundle;
}

Upvotes: 1

Related Questions