androidandroid-7.0-nougat

android.os.TransactionTooLargeException on Nougat


I updated Nexus 5X to Android N, and now when I install the app (debug or release) on it I am getting TransactionTooLargeException on every screen transition that has Bundle in extras. The app is working on all other devices. The old app that is on PlayStore and has mostly same code is working on Nexus 5X. Is anyone having the same issue?

java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 592196 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 592196 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) 

Solution

  • In the end, my problem was with things that were being saved onSaveInstance, and not with things that were being sent to the next activity. I removed all saves where I can't control the size of objects (network responses), and now it's working.

    Update 2:

    Google now provides AndroidX ViewModel which is based on the same technology as retained Fragments but much easier to use. Now ViewModel is a preferred approach.

    Update 1:

    To preserve big chunks of data, Google is suggesting to do it with Fragment that retains instance. Idea is to create an empty Fragment without a view with all necessary fields, that would otherwise be saved in the Bundle. Add setRetainInstance(true); to Fragment's onCreate method. And then save data in Fragment on Activity's onDestroy and load them onCreate. Here is an example of Activity:

    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());
        }
    }
    

    An example of Fragment:

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

    More about it, you can read here.