Reputation: 32273
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
Upvotes: 334
Views: 283371
Reputation: 91
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
.
Upvotes: 1
Reputation: 1
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.
Upvotes: 0
Reputation: 49
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.
Upvotes: -1
Reputation: 3481
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.
Upvotes: 1
Reputation: 606
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 BitmapsFragmentB
-> launches camera and returns image capture to Fragment AFragmentB
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)
Upvotes: 0
Reputation: 16122
This exception is typically thrown when the app is being sent to the background.
So I've decided to use the data Fragment pattern to completely circumvent the onSavedInstanceState
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 responsibility to my data Fragment. No need to use this on the Fragments themselves, since 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/
Upvotes: 18
Reputation: 96556
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.
Upvotes: -8
Reputation: 172
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()
}
Upvotes: -1
Reputation: 857
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" />
Upvotes: 0
Reputation: 131
Our app also has this problem. After testing, it is found that when the application memory is insufficient and the activity is recycled, the system calls the onSaveInstanceState method to save a large amount of bundle data, and the data becomes large every time, and finally a TransactionTooLargeException error will be reported, so I think of this method should be able to solve this problem.
public final int MAX_BUNDLE_SIZE = 300;
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
long bundleSize = getBundleSize(outState);
if (bundleSize > MAX_BUNDLE_SIZE * 1024) {
outState.clear();
}
}
private long getBundleSize(Bundle bundle) {
long dataSize;
Parcel obtain = Parcel.obtain();
try {
obtain.writeBundle(bundle);
dataSize = obtain.dataSize();
} finally {
obtain.recycle();
}
return dataSize;
}
Upvotes: 8
Reputation: 597
This may Happen because Activity "A" may have Fragments and when you navigate to Activity "B" .
then Activity Life Cycle of activty "A" will be
OnResume->OnPause()->OnSavedInsanceState()
here in OnSavedInsanceState may cause crash because it couldnt save state with to much data. so to try to clear the saveInsatnce of the Activity "A" by adding following code.
@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
super.onSaveInstanceState(oldInstanceState);
if (oldInstanceState != null) {
oldInstanceState.clear();
}
}
Upvotes: 8
Reputation: 11
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();
}
}
}
Upvotes: 1
Reputation: 476
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
Upvotes: 0
Reputation: 28793
@Vaiden answer helped me. I couldn't understand why short lists can raise this exceptions. I have many fragments and a couple of ViewPager
s 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 id
s. This way you can pass a list of id
s 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
.
Upvotes: 1
Reputation: 3752
For those who bitterly disappointed in search of answer of why the TransactionTooLargeException apears, try to check how much information you save in instance state.
On compile/targetSdkVersion <= 23 we have only internal warning about large size of saved state, but nothing is crashed:
E/ActivityThread: App sent too much data in instance state, so it was ignored
android.os.TransactionTooLargeException: data parcel size 713856 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:615)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3604)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3729)
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:6044)
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)
But on compile/targetSdkVersion >= 24 we have real RuntimeException crash in this case:
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 713860 bytes
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3737)
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:6044)
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 713860 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:615)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3604)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3729)
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:6044)
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)
What to do?
Save data in local database and keep only id's in instance state which you can use to retrieve this data.
Upvotes: 27
Reputation: 691
sometime Activity
include some Fragment
when Activity
need to totally recreate Fragments content,
if the sportFragmentManager.fragments
without clear history fragments
val fragments = sportFragmentManager.fragments
val transaction = sportFragmentManager.beginTransaction()
for (frag in fragments){
transaction.remove(frag)
}
transaction.commitAllowingStateLoss()
serveral times recreate Fragments the Activity
will happen (debug use tooLargeTool)
W/ActivityStopInfo: Bundle stats:
W/ActivityStopInfo: android:viewHierarchyState [size=2304]
W/ActivityStopInfo: android:views [size=2256]
W/ActivityStopInfo: android:support:fragments [size=519072]
W/ActivityStopInfo: PersistableBundle stats:
W/ActivityStopInfo: [null]
Upvotes: 1
Reputation: 655
If you converted Bitmap into Base64 in projects and save it to parcelable object you shuold resize bitmap with below code ,
replace png with jpeg and replace quality 100 to 75 or 60 :
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, byteArrayOutputStream)
this solution works for me
Upvotes: 1
Reputation: 9407
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()
}
Upvotes: 2
Reputation: 16056
This was happening in my app because I was passing a list of search results in a fragment's arguments, assigning that list to a property of the fragment - which is actually a reference to the same location in memory pointed to by the fragment's arguments - then adding new items to the list, which also changed the size of the fragment's arguments. When the activity is suspended, the base fragment class tries to save the fragment's arguments in onSaveInstanceState, which crashes if the arguments are larger than 1MB. For example:
private ArrayList<SearchResult> mSearchResults;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments() != null && getArguments().getSerializable("SearchResults") != null) {
mSearchResults = (ArrayList) getArguments().getSerializable("SearchResults");
}
}
private void onSearchResultsObtained(ArrayList<SearchResult> pSearchResults) {
// Because mSearchResults points to the same location in memory as the fragment's arguments
// this will also increase the size of the arguments!
mSearchResults.addAll(pSearchResults);
}
The easiest solution in this case was to assign a copy of the list to the fragment's property instead of assigning a reference:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments() != null && getArguments().getSerializable("SearchResults") != null) {
// Copy value of array instead of reference
mSearchResults = new ArrayList((ArrayList) getArguments().getSerializable("SearchResults"));
}
}
An even better solution would be to not pass around so much data in the arguments.
I probably never would have found this without the help of this answer and TooLargeTool.
Upvotes: 2
Reputation: 92
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);
}
}*/
Upvotes: 0
Reputation: 3629
Add this to your Activity
@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
super.onSaveInstanceState(oldInstanceState);
oldInstanceState.clear();
}
It works for me hope also it will help you
Upvotes: 7
Reputation: 1094
For me it was also the FragmentStatePagerAdapter
, however overriding saveState()
did not work. Here's how I fixed it:
When calling the FragmentStatePagerAdapter
constructor, keep a separate list of fragments within the class, and add a method to remove the fragments:
class PagerAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> items;
PagerAdapter(ArrayList<Fragment> frags) {
super(getFragmentManager()); //or getChildFragmentManager() or getSupportFragmentManager()
this.items = new ArrayList<>();
this.items.addAll(frags);
}
public void removeFragments() {
Iterator<Fragment> iter = items.iterator();
while (iter.hasNext()) {
Fragment item = iter.next();
getFragmentManager().beginTransaction().remove(item).commit();
iter.remove();
}
notifyDataSetChanged();
}
}
//...getItem() and etc methods...
}
Then in the Activity
, save the ViewPager
position and call adapter.removeFragments()
in the overridden onSaveInstanceState()
method:
private int pagerPosition;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save other view state here
pagerPosition = mViewPager.getCurrentItem();
adapter.removeFragments();
}
Lastly, in the overridden onResume()
method, re-instantiate the adapter if it isn't null
. (If it's null
, then the Activity
is being opened for the first time or after the app has been killed off by Android, in which onCreate
will do the adapter creation.)
@Override
public void onResume() {
super.onResume();
if (adapter != null) {
adapter = new PagerAdapter(frags);
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(currentTabPosition);
}
}
Upvotes: 2
Reputation: 9574
I too got this exception on a Samsung S3. I suspect 2 root causes,
Use DDMS and look at your heap as you play your app, that will give you some indication on which setcontentview is creating the issue.
I copied all the drawables across all folders to get rid of problem 2.
Issue is resolved.
Upvotes: 9
Reputation: 781
Also i was facing this issue for Bitmap data passing from one activity to another but i make a solution by making my data as static data and this is working perfect for me
In activity first :
public static Bitmap bitmap_image;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
bitmap_image=mybitmap;
}
and in second activity :
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bitmap mybitmap=first.bitmap_image;
}
Upvotes: 1
Reputation: 4041
You have clear your old InstanceState from onSaveInstanceState method, and it will work well. I am using FragmentStatePagerAdapter for my viewpager so I keep below Override method into my parent activity for clear InstanceState.
@Override
protected void onSaveInstanceState(Bundle InstanceState) {
super.onSaveInstanceState(InstanceState);
InstanceState.clear();
}
I found this solution from here android.os.TransactionTooLargeException on Nougat
Upvotes: 3
Reputation: 3261
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.
Upvotes: 1
Reputation: 361
I faced with the same issue when I tried to send bitmap via Intent and at the same time when it happens I folded the application.
How it described in this article enter link description here it happens 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.
I solved it via hack by overriding onSaveInstanceState in my Activity:
@Override
protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
}
and comment call super. It is a dirty hack but it is working perfectly. Bitmap was successfully sent without crashes. Hope this will help someone.
Upvotes: 4
Reputation: 21
Try to use EventBus
or ContentProvider
like solution.
If you are in the same process(normally all your activities would be), try to use EventBus
, cause in process data exchange does NOT need a somewhat buffer, so you do not need to worry about your data is too large. (You can just use method call to pass data indeed, and EventBus hide the ugly things)
Here is the detail:
// one side
startActivity(intentNotTooLarge);
EventBus.getDefault().post(new FooEvent(theHugeData));
// the other side
@Subscribe public void handleData(FooEvent event) { /* get and handle data */ }
If the two sides of Intent are not in the same process, try somewhat ContentProvider
.
See TransactionTooLargeException
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.
Upvotes: 1
Reputation: 5354
If you need to investigate which Parcel is causing your crash, you should consider trying TooLargeTool.
(I found this as a comment from @Max Spencer under the accepted answer and it was helpful in my case.)
Upvotes: 94
Reputation: 96
This one line of code in writeToParcel(Parcel dest, int flags) method helped me to get rid of TransactionTooLargeException.
dest=Parcel.obtain();
After this code only i am writing all data to the parcel object i.e dest.writeInt() etc.
Upvotes: 1