I got a TransactionTooLargeException
. Not reproducible. In the docs it says
The Binder transaction failed because it was too large.
D
I found the root cause of this (we got both "adding window failed" and file descriptor leak as mvds says).
There is a bug in BitmapFactory.decodeFileDescriptor()
of Android 4.4.
It only occurs when inPurgeable
and inInputShareable
of BitmapOptions
are set to true
. This causes many problem in many places interact with files.
Note that the method is also called from MediaStore.Images.Thumbnails.getThumbnail()
.
Universal Image Loader is affected by this issue. Picasso and Glide seems to be not affected. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020
I got a TransactionTooLargeException from a Stackoverflow error in a Android Espresso test. I found the stackoverflow error stack trace in the logs when I took off the Logcat filter for my app.
I'm guessing that Espresso caused the TransactionTooLargeException when trying to handle a really large exception stacktrace.
As Intents, Content Providers, Messenger, all system services like Telephone, Vibrator etc. utilize IPC infrastructure provider by Binder.Moreover the activity lifecycle callbacks also use this infrastructure.
1MB is the overall limit on all the binder transactions executed in the system at a particular moment.
In case there are lot of transactions happening when the intent is sent,it may fail even though extra data is not large. http://codetheory.in/an-overview-of-android-binder-framework/
In my case I get TransactionTooLargeException as a secondary crash after the native library crashed with SIGSEGV. The native library crash is not reported so I only receive TransactionTooLargeException.
This exception is typically thrown when the app is being sent to the background.
So I've decided to use the data Fragment method to completely circumvent the onSavedInstanceStae
lifecycle. My solution also handles complex instance states and frees memory ASAP.
First I've created a simple Fargment to store the data:
package info.peakapps.peaksdk.logic;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
/**
* A neat trick to avoid TransactionTooLargeException while saving our instance state
*/
public class SavedInstanceFragment extends Fragment {
private static final String TAG = "SavedInstanceFragment";
private Bundle mInstanceBundle = null;
public SavedInstanceFragment() { // This will only be called once be cause of setRetainInstance()
super();
setRetainInstance( true );
}
public SavedInstanceFragment pushData( Bundle instanceState )
{
if ( this.mInstanceBundle == null ) {
this.mInstanceBundle = instanceState;
}
else
{
this.mInstanceBundle.putAll( instanceState );
}
return this;
}
public Bundle popData()
{
Bundle out = this.mInstanceBundle;
this.mInstanceBundle = null;
return out;
}
public static final SavedInstanceFragment getInstance(FragmentManager fragmentManager )
{
SavedInstanceFragment out = (SavedInstanceFragment) fragmentManager.findFragmentByTag( TAG );
if ( out == null )
{
out = new SavedInstanceFragment();
fragmentManager.beginTransaction().add( out, TAG ).commit();
}
return out;
}
}
Then on my main Activity I circumvent the saved instance cycle completely, and defer the respoinsibilty to my data Fragment. No need to use this on the Fragments themselves, sice their state is added to the Activity's state automatically):
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
SavedInstanceFragment.getInstance( getFragmentManager() ).pushData( (Bundle) outState.clone() );
outState.clear(); // We don't want a TransactionTooLargeException, so we handle things via the SavedInstanceFragment
}
What's left is simply to pop the saved instance:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(SavedInstanceFragment.getInstance(getFragmentManager()).popData());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState( SavedInstanceFragment.getInstance( getFragmentManager() ).popData() );
}
Full details: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/
I have also lived TransactionTooLargeException. Firstly I have worked on understand where it occurs. I know the reason why it occurs. Every of us know because of large content. My problem was like that and I solved. Maybe this solution can be useful for anybody. I have an app that get content from api. I am getting result from API in first screen and send it to second screen. I can send this content to second screen in successful. After second screen if I want to go third screen this exception occurs. Each of my screen is created from Fragment. I noticed that when I leave from second screen. It saves its bundle content. if this content is too large this exception happens. My solution is after I got content from bundle I clear it.
class SecondFragment : BaseFragment() {
lateinit var myContent: MyContent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myContent = arguments?.getParcelable("mycontent")
arguments?.clear()
}