片段,保存onSaveInstanceState的大型数据列表(如何防止TransactionTooLargeException)

在我的应用程序中,我有ViewPager内的片段。 片段包含RecyclerView和基于用户select从Web API获取的数据列表。

在我的片段onSaveInstanceState我保存列表数据Bunde,保持configuration更改等数据

 public void onSaveInstanceState(Bundle savedState) { super.onSaveInstanceState(savedState); savedState.putParcelableArrayList(LIST_STORAGE_KEY, new ArrayList<>(mItemAdapter.getModels())); } 

现在我已经开始在应用程序错误报告中看到TransactionTooLargeException

看来在某些情况下,Im放入Bundle的列表太大(因为它是相当复杂的对象的集合)。

我应该如何处理这个案子? 如何存储(和恢复)我的片段状态。

在ViewPager中的片段上使用setRetainInstance(true)可以吗?

为了保留大量的数据,Googlebuild议使用保留实例的Fragment来实现。 想法是创造空的片段,没有所有必要的领域,否则将被保存在捆绑。 添加setRetainInstance(true) ; 到片段的onCreate方法。 而不是保存活动的onDestroy中的片段的数据,并加载他们onCreate。 这里是活动的例子:

 public class MyActivity extends Activity { private DataFragment dataFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // find the retained fragment on activity restarts FragmentManager fm = getFragmentManager(); dataFragment = (DataFragment) fm.findFragmentByTag("data"); // create the fragment and data the first time if (dataFragment == null) { // add the fragment dataFragment = new DataFragment(); fm.beginTransaction().add(dataFragment, "data").commit(); // load the data from the web dataFragment.setData(loadMyData()); } // the data is available in dataFragment.getData() ... } @Override public void onDestroy() { super.onDestroy(); // store the data in the fragment dataFragment.setData(collectMyLoadedData()); } } 

和片段的例子:

 public class DataFragment extends Fragment { // data object we want to retain private MyDataObject data; // this method is only called once for this fragment @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // retain this fragment setRetainInstance(true); } public void setData(MyDataObject data) { this.data = data; } public MyDataObject getData() { return data; } } 

如果你不想让你的片段使用setRetainInstance(true) ,那么你可以添加一个带有setRetainInstance(true)的空片段给你的活动。 这很有用,因为子片段不能使用setRetainInstance(true)

例:

 public class BaseActivity extends Activity { RetainedFragment retainedFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); retainedFragment = (RetainedFragment) getFragmentManager().findFragmentByTag("retained_fragment"); if (retainedFragment == null) { retainedFragment = new RetainedFragment(); getFragmentManager().beginTransaction().add(retainedFragment, "retained_fragment").commit(); } } public <T> T getState(String key) { //noinspection unchecked return (T) retainedFragment.map.get(key); } public void saveState(String key, Object value) { retainedFragment.map.put(key, value); } public boolean has(String key) { return retainedFragment.map.containsKey(key); } public static class RetainedFragment extends Fragment { HashMap<String, Object> map = new HashMap<>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } } } 

然后,在片段中,可以将getActivity()为Activity类,并使用saveState(String, Object)getState(String)来保存列表。


有关于此的其他讨论可以在以下位置find:

如何处理TransactionTooLargeException

Nougat上的android.os.TransactionTooLargeException (接受的答案build议setRetainInstance(true) )。

setRetainInstance()是实现这个function的最好方法,不会产生副作用。 使用static会导致内存泄漏,并且没有把onSaveInstanceState()的状态保存并返回的办法,因为setRetainInstance()为你做了这个。

因此,为片段类中的列表创build一个字段,并始终检查列表的nullsize ,以开始获取最新数据的操作