使用FragmentStatePagerAdapter并启动新活动时的TransactionTooLargeException

时间:2017-01-04 23:05:12

标签: android android-fragments android-activity fragmentstatepageradapter

这实际上是源自其他地方的问题,但我已将其缩小到以下情况。在我的MainActivity中,我使用ViewPager在底部有三个选项卡来交换我的三个片段。如果我在移动到任何片段后单击按钮移动到新活动,我会收到以下错误:

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) 

但是,如果我在ViewPager中的不同片段之间移动之前单击按钮,我就不会崩溃。我已经探索了使我的片段完全空的所有可能性,看看问题是否存在,但它似乎链接到MainActivity或FragmentStatePagerAdapater。

以下是他们的代码:

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];
    }
}

}

要清楚:我有一个按钮移动到Activity中的不同Activity,该Activity包含一个在三个片段之间移动的ViewPager。如果我在移动到另一个片段之前单击该按钮,它将加载正常。如果我移动到片段然后单击按钮,我会看到上面提到的崩溃。

我从不使用onSaveInstanceState()或类似的东西。我唯一的猜测是FragmentStatePagerAdapter将片段放入内存并导致崩溃,但我真的不知道。

2 个答案:

答案 0 :(得分:0)

TransactionTooLargeException已经困扰我们大约4个月了,我们终于解决了该问题!

发生的事情是我们在ViewPager中使用了FragmentStatePagerAdapter。用户将翻阅并创建100多个片段(其阅读应用程序)。

尽管我们在destroyItem()中正确管理了片段,但是在Android的FragmentStatePagerAdapter实现中,存在一个错误,该错误保留对以下列表的引用:

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

当Android的FragmentStatePagerAdapter尝试保存状态时,它将调用该函数

@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;
}

如您所见,即使您适当地管理FragmentStatePagerAdapter子类中的片段,基类仍将为曾经创建的每个片段存储Fragment.SavedState。当该数组转储到parcelableArray并且操作系统不希望它超过100个项目时,将发生TransactionTooLargeException。

因此,对我们而言,解决方法是重写saveState()方法,而不为“状态”存储任何内容。

@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;
}

答案 1 :(得分:0)

清除最后保存的对象实例可以解决此问题。尝试在您的活动课中使用。

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