Whenever you see TransactionTooLargeException
happening when an Activity
is in the process of stopping, that means that the Activity
was trying to send its saved state Bundles
to the system OS for safe keeping for restoration later (after a config change or process death) but that one or more of the Bundles
it sent were too large. There is a maximum limit of about 1MB for all such transactions occurring at once and that limit can be reached even if no single Bundle
exceeds that limit.
The main culprit here is generally saving too much data inside onSaveInstanceState
of either the Activity
or any Fragments
hosted by the Activity
. Typically this happens when saving something particularly large like a Bitmap
but it can also happen when sending large quantities of smaller data, like lists of Parcelable
objects. The Android team has made very clear on numerous occasions that only small amounts of view-related data should be saved in onSavedInstanceState
. However, developers have often saved pages of network data in order to make configuration changes appear as smooth as possible by not having to refetch the same data again. As of Google I/O 2017, the Android team has made clear that the preferred architecture for an Android app saves networking data
- in memory so it can be easily reused across configuration changes
- to disk so that it can be easily restored after process death and app sessions
Their new ViewModel
framework and Room
persistence library are meant to help developers fit this pattern. If your problem is with saving too much data in onSaveInstanceState
, updating to an architecture like this using those tools should fix your problem.
Personally, before updating to that new pattern I'd like to take my existing apps and just get around the TransactionTooLargeException
in the meantime. I wrote a quick library to do just that: https://github.com/livefront/bridge . It uses the same general ideas of restoring state from memory across configuration changes and from disk after process death, rather than sending all that state to the OS via onSaveInstanceState
, but requires very minimal changes to your existing code to use. Any strategy that fits those two goals should help you avoid the exception, though, without sacrificing your ability to save state.
On final note here : the only reason you see this on Nougat+ is that originally if the binder transaction limit was exceeded, the process to send the saved state to the OS would fail silently with only this error showing up in Logcat:
!!! FAILED BINDER TRANSACTION !!!
In Nougat, that silent failure was upgraded to a hard crash. To their credit, this is something the development team documented in the release notes for Nougat:
Many platform APIs have now started checking for large payloads being sent across Binder transactions, and the system now rethrows TransactionTooLargeExceptions as RuntimeExceptions, instead of silently logging or suppressing them. One common example is storing too much data in Activity.onSaveInstanceState(), which causes ActivityThread.StopInfo to throw a RuntimeException when your app targets Android 7.0.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…