use ExecutorService occur out of memory on android - android

I use ExecutorService. but sometimes occur OutOfMemoryError
error log.
java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:1063)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:921)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1339)
at kr.co.iosystem.blackeyeonandroid.m4m.domain.CapturePipeline.executeProcessor(CapturePipeline.java:195)
at kr.co.iosystem.blackeyeonandroid.m4m.domain.CapturePipeline.start(CapturePipeline.java:156)
at kr.co.iosystem.blackeyeonandroid.record.VideoCapture.start(VideoCapture.java:92)
at kr.co.iosystem.blackeyeonandroid.record.Capturing.startCapturing(Capturing.java:97)
at kr.co.iosystem.blackeyeonandroid.record.RecTimerHandler.startRecording(RecTimerHandler.java:221)
at kr.co.iosystem.blackeyeonandroid.record.RecTimerHandler.handleMessage(RecTimerHandler.java:83)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5415)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:745)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:635)
and my source
protected ExecutorService pools;
public void start() {
pools = Executors.newCachedThreadPool();
executeProcessor();
}
protected void executeProcessor() {
pools.execute(new Runnable() {
#Override
public void run() {
...
}
}
}
first, pools = Executors.newCachedTheadPool(); previously pools = Executors.newSingleThreadExecutor();
usually, this situation. how to fix OutOfMemory when I use ExecutorService on android?
if you know this problem. please advice for me
thanks

The cachedThreadPoolExecutor() will create a potentially unbounded (infinite) number of threads given new tasks. I am not sure what you have included in you run() method, however if it has a for loop/while loop or any other mechanism that spawns new work, it will automatically create a new thread costing you memory. This is what could potentially lead you to the OutOfMemoryException you are receiving.
To diagnose, try using a fixedThreadPool(int numOfThreads) with a low (bounded) number of threads e.g. 4 and see if you still get the error.
If yes, then consider what the run method is doing to consume memory
If no, then you now know that the number of threads the cachedThreadPoolExecutor() was creating was problematic, try decompose your tasks to use fewer threads. Try finding that sweet spot of thread count/ memory usage/ performance , this kind of stuff is usually about management and there rarely is a one-size fits all approach.

Related

Error: Only one Looper may be created per thread with stacktrace that has no "Caused by:"

I'm using code from Extending the Service class to handle messaging in two Services and in another class (that's not a Service).
Below is the code that initializes and gets the suspicious objects (one thread each in onCreate() in the Services, and 2 threads in a static method in the other class). Nowhere is thread.run() explicitly called (as it shouldn't be, but I know that I can make the crash happen by calling it after calling thread.start() but not by calling thread.start() twice). While this code is taken from the Android documentation, I've only altered some of the variable names in my implementation. The same is true of the message-handling code that relies on this.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
Here's the stacktrace. Unfortunately, there's nothing indicating which implementation in my code is the offender, and I'm not able to reproduce the crash.
Fatal Exception: java.lang.RuntimeException: Only one Looper may be created per thread
at android.os.Looper.prepare(Looper.java:89)
at android.os.Looper.prepare(Looper.java:84)
at android.os.HandlerThread.run(HandlerThread.java:54)
06-15 06:23:18.599 27561-29056/? E/BluetoothBoundService﹕ Interrupted
read:
java.lang.InterruptedException
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:371)
at java.lang.Thread.sleep(Thread.java:313)
at services.BluetoothBoundService.receiveDataFromBT
(BluetoothBoundService.java:1442)
at
services.BluetoothBoundService.access$2700(BluetoothBoundService.java:147)
at services.BluetoothBoundService$9.run(BluetoothBoundService.java:1173)
at java.lang.Thread.run(Thread.java:761)
06-15 06:23:18.898 28954-28975/? E/BluetoothRemoteDevices﹕
state12newState1
06-15 06:23:23.469 27561-27570/? E/System﹕ Uncaught exception thrown by
finalizer
06-15 06:23:23.470 27561-27570/? E/System﹕ java.io.IOException: socket
not created
at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:404)
at android.net.LocalSocket.shutdownInput(LocalSocket.java:207)
at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:575)
at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:273)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
at java.lang.Thread.run(Thread.java:761)
06-15 06:23:24.112 15861-15861/? E/SearchServiceStarter﹕ Task 174 failed
or timed out. Client 69758913221593243 disconnecting from SearchService!
java.util.concurrent.CancellationException: Task was cancelled.
at com.google.common.util.concurrent.d.ct(SourceFile:75)
at com.google.common.util.concurrent.d.get(SourceFile:57)
at com.google.common.util.concurrent.cg.n(SourceFile:2)
at com.google.common.util.concurrent.av.l(SourceFile:50)
at com.google.common.util.concurrent.ax.run(SourceFile:5)
at
com.google.android.apps.gsa.shared.util.concurrent.a.bc.run(SourceFile:2)
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:6119)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android....ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
06-15 06:23:24.144 15861-15861/? E/WorkerRegistryImpl﹕ getWorker() is
called after WorkerRegistry disposal.
06-15 06:23:24.153 15861-15861/? E/WorkerRegistryImpl﹕ getWorker() is
called after WorkerRegistry disposal.
06-15 06:23:34.229 15861-15861/? E/SearchServiceStarter﹕ Task 174 failed
or timed out. Client 69758913221593244 disconnecting from SearchService!
java.util.concurrent.CancellationException: Task was cancelled.
at com.google.common.util.concurrent.d.ct(SourceFile:75)
at com.google.common.util.concurrent.d.get(SourceFile:57)
at com.google.common.util.concurrent.cg.n(SourceFile:2)
at com.google.common.util.concurrent.av.l(SourceFile:50)
at com.google.common.util.concurrent.ax.run(SourceFile:5)
at
com.google.android.apps.gsa.shared.util.concurrent.a.bc.run(SourceFile:2)
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:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(Zy
It seems that somehow and somewhy Android is calling thread.run() when it shouldn't be, but I don't know how to tell what's triggering that. I've looked at the other questions and answers that propose overriding run() to prevent this from happening (for example https://stackoverflow.com/a/24115631/1493426), but I don't see how to do that by extending HandlerThread in a reasonable way that won't have undesirable, unintended consequences.
For now I'm inclined to leave everyting alone, since the code has been around for at least several months, and this crash has only been seen once, but I'd like to know if there's a safe way to make the code more crash proof without hiding any underlying problems.

ModernAsyncTask IllegalStateException

We are using AsyncTaskLoaders in our app, to load the content. We are getting this crash
java.lang.IllegalStateException: Cannot execute task: the task is already running.
at android.support.v4.content.ModernAsyncTask.doInBackground(ModernAsyncTask.java:414)
at android.support.v4.content.AsyncTaskLoader.executePendingTask(AsyncTaskLoader.java:219)
at android.support.v4.content.AsyncTaskLoader.dispatchOnCancelled(AsyncTaskLoader.java:232)
at android.support.v4.content.AsyncTaskLoader$LoadTask.onCancelled(AsyncTaskLoader.java:88)
at android.support.v4.content.ModernAsyncTask.finish(ModernAsyncTask.java:464)
at android.support.v4.content.ModernAsyncTask.access$400(ModernAsyncTask.java:48)
at android.support.v4.content.ModernAsyncTask$InternalHandler.handleMessage(ModernAsyncTask.java:483)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:211)
at android.app.ActivityThread.main(ActivityThread.java:5335)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1016)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
We have tried to reproduce this, but haven't been successful!
Is there a way to find out which Loader that is causing this?
We are using
com.android.support:support-v4:23.0.1
We are looking into the possibility that this might have a link to Activity Leaks, other than that, this is all the info we are getting.
Any ideas?
I'm still hunting this error as I can't reproduce it myself, but I believe I found the cause (at least for my case, but I have to release and see if it helps).
Turns out that when creating the observer on my loader I passed the handler as null, expecting similar beheavior as I would get if I use the default constructor for Handler (for it to run on the main thread, the thread I was creating it from).
However what happens is that it calls onChange inmediately, from whatever thread.. onChange at least in my case calls the loader's onContentChanged, that is documented to need to be called from the main thread... as that's not the case, the task and cancellationTask get screwed up (for lack of a better technical term) which causes the cancellation to be triggered in an unexpected state of the task, hence the error message.
This also manifested in my case as the loader not correctly updating entries, the cancellationTask was already loaded with a value, so the loader discarded new tasks, so the new load was skipped (takeContentChanged was false).
So make sure you are initializing your observer using new Handler() from the main thread (onStartLoading should be a fine place to do it).
Propably you have in your activity, or wrapper class reference to async task. If you have execution of this task in onClick. You can tap two times on View and you get this error.
Update
If you want to check if activity has leaked , you can use Leak Canary. This is very useful tool.
Leak canary
You can use StrictMode for finding information about leaks.
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}

Rotation bitmap causes TransactionTooLargeException in Android widget [duplicate]

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
I encountered this issue, and I found that when there huge amount of data getting exchanged between a service and an application,(This involves transferring lots of thumbnails). Actually data size was around 500kb, and the IPC transaction buffer size is set to 1024KB. I am not sure why it exceeded the transaction buffer.
This also can occur, when you pass lot of data through intent extras
When you get this exception in your application, please analyze your code.
Are you exchanging lot of data between your services and application?
Using intents to share huge data, (for example, the user selects huge number of files from gallery share press share, the URIs of the selected files will be transferred using intents)
receiving bitmap files from service
waiting for android to respond back with huge data (for example, getInstalledApplications() when the user installed lot of applications)
using applyBatch() with lot of operations pending
How to handle when you get this exception
If possible, split the big operation in to small chunks, for example, instead of calling applyBatch() with 1000 operations, call it with 100 each.
Do not exchange huge data (>1MB) between services and application
I dont know how to do this, but, Do not query android, which can return huge data :-)
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.)
This is not a definitive answer, but it may shed some light on the causes of a TransactionTooLargeException and help pinpoint the problem.
Although most answers refer to large amounts of data transferred, I see this exception being thrown incidentally after heavy scrolling and zooming and repeatedly opening an ActionBar spinner menu. The crash happens on tapping the action bar. (this is a custom mapping app)
The only data being passed around seem to be touches from the "Input Dispatcher" to the app. I think this cannot reasonably amount to anywhere near 1 mb in the "Transaction Buffer".
My app is running on a quad core 1.6 GHz device and uses 3 threads for heavylifting, keeping one core free for the UI thread. Furthermore, the app uses android:largeHeap, has 10 mb of unused heap left and has 100 mb of room left to grow the heap. So I wouldn't say it is a resource issue.
The crash is always immediately preceded by these lines:
W/InputDispatcher( 2271): channel ~ Consumer closed input channel or an error occurred. events=0x9
E/InputDispatcher( 2271): channel ~ Channel is unrecoverably broken and will be disposed!
E/JavaBinder(28182): !!! FAILED BINDER TRANSACTION !!!
Which are not neccesarily printed in that order, but (as far as I checked) happen on the same millisecond.
And the stack trace itself, for clarity, is the same as in the question:
E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException
Delving into the source code of android one finds these lines:
frameworks/base/core/jni/android_util_Binder.cpp:
case FAILED_TRANSACTION:
ALOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
To me it sounds like I'm possibly hitting this undocumented feature, where the transaction fails for other reasons than a Transaction being TooLarge. They should have named it TransactionTooLargeOrAnotherReasonException.
At this time I did not solve the issue, but if I find something useful I will update this answer.
update: it turned out my code leaked some file descriptors, the number of which is maximized in linux (typically 1024), and this seems to have triggered the exception. So it was a resource issue after all. I verified this by opening /dev/zero 1024 times, which resulted in all kinds of weird exceptions in UI related actions, including the exception above, and even some SIGSEGV's. Apparently failure to open a file/socket is not something which is handled/reported very cleanly throughout Android.
The TransactionTooLargeException has been plaguing us for about 4 months now, and we've finally resolved the issue!
What was happening was we are using a FragmentStatePagerAdapter in a ViewPager. The user would page through and create 100+ fragments (its a reading application).
Although we manage the fragments properly in destroyItem(), in Androids
implementation of FragmentStatePagerAdapter there is a bug, where it kept a reference to the following list:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
And when the Android's FragmentStatePagerAdapter attempts to save the state, it will call the function
#Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
As you can see, even if you properly manage the fragments in the FragmentStatePagerAdapter subclass, the base class will still store an Fragment.SavedState for every single fragment ever created. The TransactionTooLargeException would occur when that array was dumped to a parcelableArray and the OS wouldn't like it 100+ items.
Therefore the fix for us was to override the saveState() method and not store anything for "states".
#Override
public Parcelable saveState() {
Bundle bundle = (Bundle) super.saveState();
bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out
return bundle;
}
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.
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/
There isn't one specific cause of this problem.For me, in my Fragment class I was doing this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(R.layout.snacks_layout, container); //<-- notice the absence of the false argument
return rootView;
}
instead of this:
View rootView = inflater.inflate(R.layout.softs_layout, container, false);
It is important to understand that the transaction buffer is limited to 1 MB, regardless of device capabilities or app. This buffer is used with every API calls you make and is shared amongst all transactions an app is currently running.
I believe it also holds some specific object like parcels and such (Parcel.obtain()), so it's important to always match every obtain() with a recycle().
This error can easily happen on API calls returning a lot of data, even though the returned data is less than 1 MB (if other transactions are still running).
For example, the PackageManager.getInstalledApplication() call returns a list of all apps installed. Adding specific flags allows to retrieve a lot of extra data. Doing so is likely to fail, so it's recommended not to retrieve any extra data and retrieve those on a per-app basis.
However the call may still fail, so it's important to surround it with a catch and be able to retry if necessary.
As far as I know, there's no work-around to such issue except retrying and making sure to retrieve as little information as possible.
I too got this exception on a Samsung S3.
I suspect 2 root causes,
you have bitmaps that load and take up too much memory, use downsizing
You have some drawables missing from the drawable-_dpi folders, android looks for them in drawable, and resizes them, making your setContentView suddenly jump and use a lot of memory.
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.
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
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();
}
}
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;
}
So for us it was we were trying to send too large of an object through our AIDL interface to a remote service. The transaction size cannot exceed 1MB. The request is broken down into separate chunks of 512KB and sent one at a time through the interface. A brutal solution I know but hey - its Android :(
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.
Recently I also have encountered with an interesting case while working with Android's Contacts Provider.
I needed to load photos of contacts from internal contacts database and according to the system architecture all of this data are delivered by queries to Contacts Provider.
As it works as a separate application - all kinds of data transferring are performed by using Binder mechanism and so Binder buffer comes into play here.
My main mistake was that I didn't close the Cursor with blob data gotten from Contacts Provider, so that the memory allocated for the provider increased and this inflated the Binder buffer until I got tons of !!!FAILED BINDER TRANSACTION!!! messages in my LogCat output.
So the main idea is that when you work with external Content Providers and got Cursors from them, always close it when you finish to work with them.
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
Make sure that you do not put into Intent object data of large size. In my case I was adding String 500k size and then starting another activity. It always failed with this exception. I avoided sharing data between activities by using static variables of activities - you don't have to send them to Intent and then pulling from it.
What I had:
String html = new String();//some string of 500K data.
Intent intent = new Intent(MainActivity.this, PageWebView.class);
//this is workaround - I just set static variable and then access it from another activity.
MainActivity.htmlBody = timelineDb.getHTMLBodyForTweet(tweet);
//This line was present and it actually failed with the same exception you had.
//intent.putExtra("com.gladimdim.offtie.webview", html);
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.
I got this in my syncadapter when trying to bulkInsert a large ContentValues[]. I decided to fix it as follows:
try {
count = provider.bulkInsert(uri, contentValueses);
} catch (TransactionTooLarge e) {
int half = contentValueses.length/2;
count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, 0, half));
count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, half, contentValueses.length));
}
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);
}
}
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.
When I am dealing with the WebView in my app it happens. I think it's related to addView and UI resources.
In my app I add some code in WebViewActivity like this below then it runs ok:
#Override
protected void onDestroy() {
if (mWebView != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}
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
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.
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.
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.
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;
}
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()
}
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
sometime Activity include some Fragment when Activityneed 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]

RunOnUiThread inside loop efficiency

Is it fine to do following if we think about efficiency?
new Runnable() {
#Override
public void run() {
try {
final Runnable uitask = new Runnable() {
public void run() {
//sth that modifies UI
}
};
for (...) {
context.runOnUiThread(uitask);
Thread.sleep(...);
}
} catch (Throwable t) {
}
}
}
I'm most concerned about that runOnUi might create new thread each time, which would be very bad? Am I right or it smarter like ThreadPool?
Well, runOnUiThread doesn't create new threads, it is starting your runnable on the existing UI thread.
but instead of your code you could just do
void onCreate(..) {
...
mHandler = new Handler();
}
for (...) {
mHandler.postDelayed(uitask, DELAY * i++);
}
I'm most concerned about that runOnUi might create new thread each
time, which would be very bad?
Well. It depends what you want to achieve. In Android, native threads are not much efficient. There are more effective ways how to do background work (SDK provides them).
If you want to do some periodical work you can use Handler to achieve your goal. Just create only one Runnable and then you can send request for periodical callbacks. Another solution is to use Services which run in background. They're directly designated for long-running tasks and provide very handy way for background processing.
Sometimes you need to inform user with some information about current progress in your tasks so in this case you can simply use mentioned Handler or AsyncTask.
I think your solution is not clean and efficient. If you'll create for example 40 Threads, your work won't be faster or more efficient at all. Your GB will scream and application will take the greater part of CPU and high battery consumption is something you shouldn't allow.

What to do on TransactionTooLargeException

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
I encountered this issue, and I found that when there huge amount of data getting exchanged between a service and an application,(This involves transferring lots of thumbnails). Actually data size was around 500kb, and the IPC transaction buffer size is set to 1024KB. I am not sure why it exceeded the transaction buffer.
This also can occur, when you pass lot of data through intent extras
When you get this exception in your application, please analyze your code.
Are you exchanging lot of data between your services and application?
Using intents to share huge data, (for example, the user selects huge number of files from gallery share press share, the URIs of the selected files will be transferred using intents)
receiving bitmap files from service
waiting for android to respond back with huge data (for example, getInstalledApplications() when the user installed lot of applications)
using applyBatch() with lot of operations pending
How to handle when you get this exception
If possible, split the big operation in to small chunks, for example, instead of calling applyBatch() with 1000 operations, call it with 100 each.
Do not exchange huge data (>1MB) between services and application
I dont know how to do this, but, Do not query android, which can return huge data :-)
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.)
This is not a definitive answer, but it may shed some light on the causes of a TransactionTooLargeException and help pinpoint the problem.
Although most answers refer to large amounts of data transferred, I see this exception being thrown incidentally after heavy scrolling and zooming and repeatedly opening an ActionBar spinner menu. The crash happens on tapping the action bar. (this is a custom mapping app)
The only data being passed around seem to be touches from the "Input Dispatcher" to the app. I think this cannot reasonably amount to anywhere near 1 mb in the "Transaction Buffer".
My app is running on a quad core 1.6 GHz device and uses 3 threads for heavylifting, keeping one core free for the UI thread. Furthermore, the app uses android:largeHeap, has 10 mb of unused heap left and has 100 mb of room left to grow the heap. So I wouldn't say it is a resource issue.
The crash is always immediately preceded by these lines:
W/InputDispatcher( 2271): channel ~ Consumer closed input channel or an error occurred. events=0x9
E/InputDispatcher( 2271): channel ~ Channel is unrecoverably broken and will be disposed!
E/JavaBinder(28182): !!! FAILED BINDER TRANSACTION !!!
Which are not neccesarily printed in that order, but (as far as I checked) happen on the same millisecond.
And the stack trace itself, for clarity, is the same as in the question:
E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException
Delving into the source code of android one finds these lines:
frameworks/base/core/jni/android_util_Binder.cpp:
case FAILED_TRANSACTION:
ALOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
To me it sounds like I'm possibly hitting this undocumented feature, where the transaction fails for other reasons than a Transaction being TooLarge. They should have named it TransactionTooLargeOrAnotherReasonException.
At this time I did not solve the issue, but if I find something useful I will update this answer.
update: it turned out my code leaked some file descriptors, the number of which is maximized in linux (typically 1024), and this seems to have triggered the exception. So it was a resource issue after all. I verified this by opening /dev/zero 1024 times, which resulted in all kinds of weird exceptions in UI related actions, including the exception above, and even some SIGSEGV's. Apparently failure to open a file/socket is not something which is handled/reported very cleanly throughout Android.
The TransactionTooLargeException has been plaguing us for about 4 months now, and we've finally resolved the issue!
What was happening was we are using a FragmentStatePagerAdapter in a ViewPager. The user would page through and create 100+ fragments (its a reading application).
Although we manage the fragments properly in destroyItem(), in Androids
implementation of FragmentStatePagerAdapter there is a bug, where it kept a reference to the following list:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
And when the Android's FragmentStatePagerAdapter attempts to save the state, it will call the function
#Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
As you can see, even if you properly manage the fragments in the FragmentStatePagerAdapter subclass, the base class will still store an Fragment.SavedState for every single fragment ever created. The TransactionTooLargeException would occur when that array was dumped to a parcelableArray and the OS wouldn't like it 100+ items.
Therefore the fix for us was to override the saveState() method and not store anything for "states".
#Override
public Parcelable saveState() {
Bundle bundle = (Bundle) super.saveState();
bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out
return bundle;
}
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.
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/
There isn't one specific cause of this problem.For me, in my Fragment class I was doing this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(R.layout.snacks_layout, container); //<-- notice the absence of the false argument
return rootView;
}
instead of this:
View rootView = inflater.inflate(R.layout.softs_layout, container, false);
It is important to understand that the transaction buffer is limited to 1 MB, regardless of device capabilities or app. This buffer is used with every API calls you make and is shared amongst all transactions an app is currently running.
I believe it also holds some specific object like parcels and such (Parcel.obtain()), so it's important to always match every obtain() with a recycle().
This error can easily happen on API calls returning a lot of data, even though the returned data is less than 1 MB (if other transactions are still running).
For example, the PackageManager.getInstalledApplication() call returns a list of all apps installed. Adding specific flags allows to retrieve a lot of extra data. Doing so is likely to fail, so it's recommended not to retrieve any extra data and retrieve those on a per-app basis.
However the call may still fail, so it's important to surround it with a catch and be able to retry if necessary.
As far as I know, there's no work-around to such issue except retrying and making sure to retrieve as little information as possible.
I too got this exception on a Samsung S3.
I suspect 2 root causes,
you have bitmaps that load and take up too much memory, use downsizing
You have some drawables missing from the drawable-_dpi folders, android looks for them in drawable, and resizes them, making your setContentView suddenly jump and use a lot of memory.
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.
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
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();
}
}
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;
}
So for us it was we were trying to send too large of an object through our AIDL interface to a remote service. The transaction size cannot exceed 1MB. The request is broken down into separate chunks of 512KB and sent one at a time through the interface. A brutal solution I know but hey - its Android :(
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.
Recently I also have encountered with an interesting case while working with Android's Contacts Provider.
I needed to load photos of contacts from internal contacts database and according to the system architecture all of this data are delivered by queries to Contacts Provider.
As it works as a separate application - all kinds of data transferring are performed by using Binder mechanism and so Binder buffer comes into play here.
My main mistake was that I didn't close the Cursor with blob data gotten from Contacts Provider, so that the memory allocated for the provider increased and this inflated the Binder buffer until I got tons of !!!FAILED BINDER TRANSACTION!!! messages in my LogCat output.
So the main idea is that when you work with external Content Providers and got Cursors from them, always close it when you finish to work with them.
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
Make sure that you do not put into Intent object data of large size. In my case I was adding String 500k size and then starting another activity. It always failed with this exception. I avoided sharing data between activities by using static variables of activities - you don't have to send them to Intent and then pulling from it.
What I had:
String html = new String();//some string of 500K data.
Intent intent = new Intent(MainActivity.this, PageWebView.class);
//this is workaround - I just set static variable and then access it from another activity.
MainActivity.htmlBody = timelineDb.getHTMLBodyForTweet(tweet);
//This line was present and it actually failed with the same exception you had.
//intent.putExtra("com.gladimdim.offtie.webview", html);
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.
I got this in my syncadapter when trying to bulkInsert a large ContentValues[]. I decided to fix it as follows:
try {
count = provider.bulkInsert(uri, contentValueses);
} catch (TransactionTooLarge e) {
int half = contentValueses.length/2;
count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, 0, half));
count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, half, contentValueses.length));
}
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);
}
}
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.
When I am dealing with the WebView in my app it happens. I think it's related to addView and UI resources.
In my app I add some code in WebViewActivity like this below then it runs ok:
#Override
protected void onDestroy() {
if (mWebView != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}
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
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.
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.
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.
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;
}
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()
}
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
sometime Activity include some Fragment when Activityneed 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]

Categories

Resources