337

I got a TransactionTooLargeException. Not reproducible. In the docs it says

The Binder transaction failed because it was too large.

During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown.

...

There are two possible outcomes when a remote procedure call throws TransactionTooLargeException. Either the client was unable to send its request to the service (most likely if the arguments were too large to fit in the transaction buffer), or the service was unable to send its response back to the client (most likely if the return value was too large to fit in the transaction buffer).

...

So somewhere I'm passing or receiving arguments which exceed some unknown limit. Where?

The stacktrace doesn't show anything useful:

java.lang.RuntimeException: Adding window failed
at android.view.ViewRootImpl.setView(ViewRootImpl.java:548)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:406)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:320)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:152)
at android.view.Window$LocalWindowManager.addView(Window.java:557)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2897)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4977)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.view.IWindowSession$Stub$Proxy.add(IWindowSession.java:569)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
... 16 more
android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.view.IWindowSession$Stub$Proxy.add(IWindowSession.java:569)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:406)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:320)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:152)
at android.view.Window$LocalWindowManager.addView(Window.java:557)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2897)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4977)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)

It seems to be related with views? How is this related to remote procedure call?

Maybe important: Android version: 4.0.3, Device: HTC One X

8
  • No. But I didn't get it again. Have error tracker in the live app and got it only this one time in about 3 weeks. At least it doesn't seem to happen frequently. Maybe worth to open an issue at Android although... Commented Aug 10, 2012 at 18:31
  • I don't have an answer but this reliably causes my Galaxy S2 to hard reset. Commented Sep 25, 2012 at 10:15
  • I have just had this occur in one of my applications today. This has also only happened once and with a Galaxy S3. Its interesting that this only seems to be apprehending with the more powerful devices. Commented Sep 26, 2012 at 8:20
  • that exception was added in API 15, developer.android.com/reference/android/os/… And I have reproduced it in the MapView while scrolling around the map. until the gc wrote that I have no memory left.(It took me couple of minutes) Commented Dec 9, 2012 at 13:45
  • The transaction buffer is limited to 1MB on all devices, and this buffer olds every transaction. So the more powerful the device the more transactions it may perform simultaneously, all of which consuming the same 1MB buffer. That said, your answer is not an answer but a comment. Commented Nov 23, 2013 at 16:21

48 Answers 48

1
2
1

@Vaiden answer helped me. I couldn't understand why short lists can raise this exceptions. I have many fragments and a couple of ViewPagers with lists. So, every time I started another activity or stopped a program (turned off a screen) I catched this exception (usually on Xiaomi).

I found that all fragments called their onSaveInstanceState() events, and in the end hosting activity called onSaveInstanceState() before onStop(). After that a crash occured. So, I understood that many short lists (10-100 Kb in size) can raise TransactionTooLargeException exception.

You may overcome this problem with writing data to a database and then get items by their ids. This way you can pass a list of ids to an Activity/Fragment.

But if you wish a temporary fast method, do this.

1) Create a retain-instance fragment, that will survive during activity recreation.

class SavedInstanceFragment : Fragment() {

    // This map will hold bundles from different sources (tag -> bundle).
    private lateinit var bundleMap: HashMap<String, Bundle?>


    // This method is only called once for this fragment.
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        retainInstance = true
        bundleMap = HashMap()
    }

    fun pushData(key: String, bundle: Bundle): SavedInstanceFragment {
        if (bundleMap[key] == null) {
            bundleMap[key] = bundle
        } else {
            bundleMap[key]!!.putAll(bundle)
        }
        return this
    }

    fun popData(key: String): Bundle? {
        val data = bundleMap[key]
        bundleMap[key] = null
        return data
    }

    fun removeData(key: String) {
        bundleMap.remove(key)
    }


    companion object {

        private val TAG = SavedInstanceFragment::class.java.simpleName

        // Create the fragment with this method in `onCreate()` of an activity.
        // Then you can get this fragment with this method again.
        fun getInstance(fragmentManager: FragmentManager): SavedInstanceFragment {
            var out = fragmentManager.findFragmentByTag(TAG) as SavedInstanceFragment?

            if (out == null) {
                out = SavedInstanceFragment()
                fragmentManager.beginTransaction().add(out, TAG).commit()
            }
            return out
        }
    }
}

2) In onCreate() of your activities that hold problem fragments, create this fragment. It will survive activity recreations. You should create it before other fragments are added to the FragmentManager, because this operation is asynchronous.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity)

    if (savedInstanceState == null) {
        SavedInstanceFragment.getInstance(supportFragmentManager)
    }

    ...
}

3) In every problem fragment add these lines. You should access a retain-instance fragment with getInstance(activity!!.supportFragmentManager) everywhere, so that you can find it inside FragmentManager. If use childFragmentManager as a parameter of getInstance(), then you will create another fragment and crash.

Also clear arguments after you read them and set a bundle, for instance, in onSaveInstanceState: arguments?.clear(). This is very important because arguments stay in memory when you create a new fragment. When you later return to the current fragment and rotate a screen, data won't be lost because they are already contained in a bundle and will be read in onCreate.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val bundle = if (savedInstanceState == null) {
        // Remove old data in order not to show wrong information.
        SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).removeData(TAG)
        null
    } else {
        SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).popData(TAG)
    }
    (bundle ?: arguments)?.run { // I access 'bundle' or 'arguments', depending if it is not first or first call of 'onCreate()'.
        token = getString(ARG_TOKEN)!!
        id = getInt(ARG_ID)
        items = getParcelableArrayList(ARG_ITEMS)
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    // Create a copy of savedInstanceState and push to the retain-instance fragment.
    val bundle = (outState.clone() as Bundle).apply {
        putString(ARG_TOKEN, token)
        putInt(ARG_ID, id)
        putParcelableArrayList(ARG_ITEMS, items)
    }
    SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).pushData(TAG, bundle)
    arguments?.clear() // Avoids an exception "java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size xxxxxxx bytes".
    super.onSaveInstanceState(outState)
}


companion object {

    private val TAG = YourFragment::class.java.simpleName

    private const val ARG_TOKEN = "ARG_TOKEN"
    private const val ARG_ID = "ARG_ID"
    private const val ARG_ITEMS = "ARG_ITEMS"

    fun newInstance(token: String, id: Int, items: ArrayList<Item>?) =
        YourFragment().apply {
            arguments = Bundle().apply {
                putString(ARG_TOKEN, token)
                putInt(ARG_ID, id)
                putParcelableArrayList(ARG_ITEMS, items)
            }
        }
}

Instead of persistent fragment you can use Singleton. Also read https://medium.com/@mdmasudparvez/android-os-transactiontoolargeexception-on-nougat-solved-3b6e30597345 for more information about libraries, solutions.

I suppose, this solution won't work if your activities are removed after starting other activities. You can check this behaviour when turn on a checkbox Do not keep activities in Developer options (don't forget to uncheck after). In this case getFragmentManager() will be new and you won't get old data. When you return from the new activity, you will get null in a bundle.

Sign up to request clarification or add additional context in comments.

Comments

1

Issue is resolved by:

 Bundle bundle = new Bundle();
  bundle.putSerializable("data", bigdata);
...
  CacheHelper.saveState(bundle,"DATA");
  Intent intent = new Intent(mActivity, AActivity.class);
  startActivity(intent, bb);// do not put data to intent.

In Activity:
   @Override
   protected void onCreate(Bundle savedInstanceState) {
        Bundle bundle = CacheHelper.getInstance().loadState(Constants.DATA);
        if (bundle != null){
            Intent intent = getIntent();
            intent.putExtras(bundle);
        }
        getIntent().getExtra(..);
        ....
   }
   @Override
    protected void onDestroy() {
        super.onDestroy();
        CacheHelper.clearState("DATA");
    }

public class CacheHelper {

    public static void saveState(Bundle savedInstanceState, String name) {
        Bundle saved = (Bundle) savedInstanceState.clone();
        save(name, saved);
    }
    public Bundle loadState(String name) {

        Object object = load(name);
        if (object != null) {
            Bundle bundle = (Bundle) object;
            return bundle;
        }
        return null;
    }
    private static void save(String fileName, Bundle object) {
        try {
            String path = StorageUtils.getFullPath(fileName);
            File file = new File(path);
            if (file.exists()) {
                file.delete();
            }
            FileOutputStream fos = new FileOutputStream(path, false);

            Parcel p = Parcel.obtain(); //creating empty parcel object
            object.writeToParcel(p, 0); //saving bundle as parcel
            //parcel.writeBundle(bundle);
            fos.write(p.marshall()); //writing parcel to file

            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static Bundle load(String fileName) {
        try {
            String path = StorageUtils.getFullPath(fileName);
            FileInputStream fis = new FileInputStream(path);

            byte[] array = new byte[(int) fis.getChannel().size()];
            fis.read(array, 0, array.length);

            Parcel parcel = Parcel.obtain(); //creating empty parcel object
            parcel.unmarshall(array, 0, array.length);
            parcel.setDataPosition(0);
            Bundle out = parcel.readBundle();
            out.putAll(out);

            fis.close();
            parcel.recycle();
            return out;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
public static void clearState(Activity ac) {
    String name = ac.getClass().getName();
    String path = StorageUtils.getFullPath(name);
    File file = new File(path);
    if (file.exists()) {
        file.delete();
    }
}
}

Comments

1

I got this error while uninstalling the app to which another app was making an IPC call to.

A hypothesis is that; as the "serving" app disappeared because of uninstallation, some transmit buffer might have been filled up, causing this error because nothing was no longer "popping things off" from the buffer.

If that was the case in your case, this problem might be nothing to worry about.

Comments

1

I had this issue with NavHostFragment. I would switch between different fragments a couple of times then press home button and the application would crash. It would list all the fragments I've switched to which was somewhat confusing because NavHostFragment actually recreates fragment on navigate calls. I initially thought I had a memory leak somewhere but it later turned out that those were back stack entries and not actual fragments. The NavHostFragment was leaking memory for me.

In my app I made it so that user can switch between different fragments using a NavigationView with DrawerLayout. With this setup, you usually make the burger button with fragment options in action bar turn into a back arrow once you select a fragment that is not the initial destination but I specifically wanted to have navigation from any fragment to any other fragment. It turns out, method navigate in NavController of NavHostFragment always creates a NavBackStackEntry, this behaviour can't be suppressed. I assume I've misused NavHostFragment and it's always meant to represent transitions made by user as a tree without loops. By introducing a loop I made it that the back stack entries would be created indefinitely without being popped. The application tried to serialize all these back stack traces and failed miserably.

My solution to this was to additionally try and pop all of the back stack entries that reference target destination. I went from something like this:

NavController navController = navHostFragment.getNavController();
final int destinationId = ...;
navController.navigate(destinationId);

To something like this:

NavController navController = navHostFragment.getNavController();
final int destinationId = ...;
NavOptions.Builder navOptionsBuilder = new NavOptions.Builder();
navOptionsBuilder.setLaunchSingleTop(true);
navOptionsBuilder.setPopUpTo(destinationId, /* inclusive = */ true, false);
navController.navigate(destinationId, null, navOptionsBuilder.build());

One downside to this is that there are gonna be a lot of annoying warnings in the LogCat since every other time there won't be a back stack entry for the target destination. This can be fixed by calling setPopUpTo with inclusive = false instead of true.

1 Comment

haha, same here... why didn't i saw that answer 2 hours earlier :-D
0

A solution would be for the app to write the ArrayList (or whatever object is causing the problem) to the file system, then pass a reference to that file (e.g., filename/path) via the Intent to the IntentService and then let the IntentService retrieve the file contents and convert it back to an ArrayList.

When the IntentService has done with the file, it should either delete it or pass the instruction back to the app via a Local Broadcast to delete the file that it created (passing back the same file reference that was supplied to it).

For more info see my answer to this related problem.

Comments

0

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/

Comments

0

With so many places where TransactionTooLargeException can happen-- here's one more new to Android 8--a crash when someone merely starts to type into an EditText if the content is too big.

It's related to the AutoFillManager (new in API 26) and the following code in StartSessionLocked():

    mSessionId = mService.startSession(mContext.getActivityToken(),
            mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
            mCallback != null, flags, mContext.getOpPackageName());

If I understand correctly, this calls the autofill service-- passing the AutofillManagerClient within the binder. And when the EditText has a lot of content, it seems to cause the TTLE.

A few things may mitigate it (or did as I was testing anyway): Add android:importantForAutofill="noExcludeDescendants" in the EditText's xml layout declaration. Or in code:

EditText et = myView.findViewById(R.id.scriptEditTextView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    et.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}

A 2nd awful, terrible workaround might also be to override the performClick() and onWindowFocusChanged() methods to catch the error in a TextEdit subclass itself. But I don't think that's wise really...

Comments

0

For me TransactionTooLargeException occurred when i tried to send large bitmap image from one activity to another via intent. I solved this problem by using Application's Global Variables.

For example if you want to send large bitmap image from an activity A to to activity B, then store that bitmap image in global variable

((Global) this.getApplication()).setBitmap(bitmap);

then start activity B and read from global variable

Bitmap bitmap = ((Global) this.getApplication()).getBitmap();

Comments

0

For me this error was coming in presenter. I made comment to onResume and write the same code in onStart and it worked for me.

 @Override
    public void onStart() {
        super.onStart();
        Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
        if (goal != null && goal.getValue() > 0) {
            mCurrentValue = (int) goal.getValue();
            notifyPropertyChanged(BR.currentValue);
            mIsButtonEnabled.set(true);
        }
    }
   /* @Override
    public void onResume() {
        super.onResume();
        Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
        if (goal != null && goal.getValue() > 0) {
            mCurrentValue = (int) goal.getValue();
            notifyPropertyChanged(BR.currentValue);
            mIsButtonEnabled.set(true);
        }
    }*/

Comments

0

In my case the reason of TransactionTooLargeException was that I sent big data to fragment in arguments (using Bundle), like this:

        var arguments = Bundle()
        arguments.putSerializable("argumentName", argumentValue)
        fragment.arguments = arguments

It works fine only if argumentValue has small size (for example Int or String), but if it has big size (for example list of DTO) - you can get TransactionTooLargeException. So now I pass parameters to fragment in constructor, and everything works fine.

PS Thanks to sulai for TooLargeTool

1 Comment

It's a wrong solution, because you should pass small data through arguments. If you add them in constructor, they won't retain after configuration changes (rotate screen, etc.). Large data should be passed through database, SharedPreferences, ViewModel and so on.
0

I faced same issue with ViewPager2 and FragmentStateAdapter the problem here is FragmentStateAdapter marked 'saveState()' as final and ViewPager2 class is final too, so we can not override the methods as suggested in other answers making the existing answer not applicable.

In my case I was able to get rid of Transaction too large issue by not saving state of pager by adding \android:saveEnabled="false" ' in XML entry for the View pager

<androidx.viewpager2.widget.ViewPager2
    android:saveEnabled="false"
    android:id="@+id/viewPager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

Comments

0

I have also come across this problem recently and after 2 days of reading all the solutions on this and other sites, I would like to share the cause of my problem and how I fixed it, as I haven't seen my problem in particular.

The fragments I use are:

  • FragmentA -> contains a recycler view with Bitmaps
  • FragmentB -> launches camera and returns image capture to Fragment A

FragmentB has the camera stuff so I send the Bitmap to FragmentA using previousBackStackEntry like this:

findNavController().run {
    previousBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, photo)
    popBackStack()
}

Finally, I use the Bitmap on FragmentA doing:

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Bitmap>(KEY_REPORT_PHOTO)
    ?.observe(viewLifecycleOwner) {
        it?.let { photo ->
            viewModel.addPhoto(photo)
            findNavController().currentBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, null)
        }
    }

Reading the information on this page, my first approach was to find out what was going on in onSaveInstanceState and try to delete the bitmaps from the recycler view that I thought were being saved when I closed the app and put it in the background.

Finally I realised that the problem was the Bitmap I was adding in the BackStackEntry of the navigation component and the solution was as easy as cleaning it after using the Bitmap in the FragmentA with this line:

findNavController().currentBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, null)

Comments

0

I don't know who may need this, but in case that you are using ViewPager2 with FragmentStateAdapter, and you have a large amount of fragments that also have their own Bundle data persistence, take care that you are not using Activity to instantiate FragmentStateAdapter, but your parent Fragment.

class PostPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

Reason is that if you kill the fragment that hosts the ViewPager fragments, activity will still keep these fragments alive, which in time will consume memory and not only throw this exception but maybe many others.

This was at least issue on my end, and I figured it our using TooLargeTool along with debugging activity fragments.

3 Comments

I use fragment as parameter in constructor of FragmentStateAdapter, but the issue still occurre :(
i am facing same issue but i cant understand your solution how can i use fragment instance if my view pager is in activity
@MateenChaudhry, probably you can add one fragment and ViewPager2 inside.
0

I also facing this issue.

I am sending a large data via intent.

ArrayList<MyObj>list = new ArrayList<>();

myintent.putExtra("data",list); //This line is causing the TransactionTooLargeException

Instead of sending the data in intent, I make a static global variable and use that variable in both activity.

public static ArrayList<MyObj>list = new ArrayList<>();

This is how i resolve the issue.

Comments

0

I had this issue in my jetpack compose application and the problem for this crash was navigation back stack accumulation. Each time users navigate to a new screen using navController.navigate(route), a new back stack entry is created. After navigating through many pages, the app saves many navigation entries in the bundle, and even more if you use nested navigation, with each entry preserving the entire navigation history.

To solve this problem you can add navigation flags to prevent stack accumulation:

navController.navigate(route) {    
   launchSingleTop = true    
   // Or for complete replacement:    
   popUpTo(currentRoute) { inclusive = true }
}

Comments

-1

If you are using ViewPager2 FragmentStateAdapter with Navigation Component:

I was creating the adapter in onViewCreated(), but the view gets recreated every time you navigate back. For some reason, the old adapter wasn't detaching properly and increasing the bundle size until the error occurs. I debug it with TooLargeTool and the solution was to avoid the adapter recreation in onViewCreated().

In my fragment, I have the adapter variable:

var pagerAdapter:HomePagerAdapter?=null

Inside onViewCreated method, I create the adapter only once:

if(pagerAdapter == null){
  pagerAdapter = HomePagerAdapter(childFragmentManager, lifecycle, myContent)
}

To prevent IllegalArgumentException, I manually detach the adapter from the pager inside onDestroyView:

override fun onDestroyView() {
   pager.adapter = null
   super.onDestroyView()
}

Comments

-1

add this code on method OnViewCreated

childFragmentManager.apply {
        logi("childFragmentManager = ", fragments.size)
        for (fragment in fragments) {
            logi(fragment)
            beginTransaction().remove(fragment).commitAllowingStateLoss()
        }
    }

In my case, the viewpager2 was create a lot of fragment, it's not clean up after the screen rotation, that's why the number of fragment increment too large (even not over 1kB) but this bug still occur. Hope this code can help someone.

Comments

-8

Another possible cause:

I had an activity that was starting itself in onResume()! This results in a ton of transactions and causes the phone (Galaxy S2) to completely freeze (no ANR or anything) and then to hard reset, which is kind of a huge bug in itself.

It would be interesting to see what happens on other phones with this code:

class MyActivity extends Activity
{
  // ...
  @Override
  void onResume()
  {
     super.onResume()
     startActivity(new Intent(this, MyActivity.class));
  }
}

I am not saying you should use that code.

6 Comments

This is not device related. You have just created an infinite loop. You create MyActivity that starts a new MyActivity when it's displayed that starts a new MyActivity and so on... What did you even want to achieve here?
It shouldn't cause the phone to freeze and reboot.
@GaborPeto is right. This will create infinite loop.
Yes and it shouldn't cause the phone to freeze and reboot.
How is everyone so bad at reading comprehension? I wasn't saying you should use the code in my answer. I was just saying that if you did have that code because of a mistake, it could cause a TransactionTooLargeException. And as an aside it also causes the phone to reboot which should not happen. Clear?
|
1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.