Qué hacer en TransactionTooLargeException


239

Tengo una TransactionTooLargeException. No reproducible. En los documentos dice

La transacción de Binder falló porque era demasiado grande.

Durante una llamada a procedimiento remoto, los argumentos y el valor de retorno de la llamada se transfieren como objetos Parcel almacenados en el búfer de transacciones de Binder. Si los argumentos o el valor de retorno son demasiado grandes para caber en el búfer de transacciones, la llamada fallará y se lanzará TransactionTooLargeException.

...

Hay dos resultados posibles cuando una llamada a procedimiento remoto arroja TransactionTooLargeException. O el cliente no pudo enviar su solicitud al servicio (muy probablemente si los argumentos eran demasiado grandes para caber en el búfer de transacciones) o el servicio no pudo enviar su respuesta al cliente (muy probablemente si el valor de retorno era demasiado grande para caber en el búfer de transacciones).

...

Entonces, en algún lugar estoy pasando o recibiendo argumentos que exceden algún límite desconocido. ¿Dónde?

El stacktrace no muestra nada útil:

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)

Parece estar relacionado con las vistas? ¿Cómo se relaciona esto con la llamada a procedimiento remoto?

Quizás importante: versión de Android: 4.0.3, dispositivo: HTC One X


No. Pero no lo entendí de nuevo. Tenga un rastreador de errores en la aplicación en vivo y lo obtuve solo esta vez en aproximadamente 3 semanas. Al menos no parece suceder con frecuencia. Quizás valga la pena abrir un problema en Android, aunque ...
Ixx

No tengo una respuesta, pero esto hace que mi Galaxy S2 se reinicie de manera confiable.
Timmmm

Acabo de hacer que esto ocurra en una de mis aplicaciones hoy. Esto también solo sucedió una vez y con un Galaxy S3. Es interesante que esto solo parezca aprehender con los dispositivos más potentes.
danwms

esa excepción se agregó en API 15, developer.android.com/reference/android/os/… Y la he reproducido en MapView mientras me desplazaba por el mapa. hasta que el gc escribió que no me queda memoria (me tomó un par de minutos)
meh

El búfer de transacción está limitado a 1 MB en todos los dispositivos, y este búfer vence cada transacción. Por lo tanto, cuanto más potente es el dispositivo, más transacciones puede realizar simultáneamente, todo lo cual consume el mismo búfer de 1 MB. Dicho esto, su respuesta no es una respuesta sino un comentario.
3c71

Respuestas:


158

Encontré este problema y descubrí que cuando se intercambia una gran cantidad de datos entre un servicio y una aplicación, (esto implica transferir muchas miniaturas). En realidad, el tamaño de los datos era de alrededor de 500 kb, y el tamaño del búfer de transacciones IPC se establece en 1024 KB. No estoy seguro de por qué excedió el búfer de transacciones.

Esto también puede ocurrir cuando pasa muchos datos a través de intentos adicionales

Cuando reciba esta excepción en su aplicación, analice su código.

  1. ¿Intercambia muchos datos entre sus servicios y la aplicación?
  2. Usando intentos para compartir grandes cantidades de datos (por ejemplo, el usuario selecciona una gran cantidad de archivos del recurso compartido de prensa de la galería, los URI de los archivos seleccionados se transferirán mediante intentos)
  3. recibir archivos de mapa de bits del servicio
  4. esperando que Android responda con grandes datos (por ejemplo, getInstalledApplications () cuando el usuario instaló muchas aplicaciones)
  5. usando applyBatch () con muchas operaciones pendientes

Cómo manejar cuando obtienes esta excepción

Si es posible, divida la operación grande en fragmentos pequeños, por ejemplo, en lugar de llamar a applyBatch () con 1000 operaciones, llámelo con 100 cada una.

No intercambie grandes datos (> 1 MB) entre servicios y aplicaciones

No sé cómo hacer esto, pero, no consultes Android, que puede devolver datos enormes :-)


1
¿Y si estoy comprobando que mi .apk está instalado o no? al momento de la instalación ... Recibo la misma excepción al verificar mi paquete com.test.installedornot.Mi tamaño de .apk es más de 9MB, entonces en ese caso ¿cómo manejaré esta excepción?
DJhon

15
Recibo esta excepción exactamente durante una llamada a getInstalledApplications. ¿Qué se puede hacer para resolver esto?
Stan

1
@Stan Esta API es común y se usa ampliamente en la aplicación de Android. Esta excepción realmente me preocupa de alguna manera cuando uso esta API.
Peacepassion

77
Puedo confirmar sus conclusiones sobre el límite de estar en algún lugar alrededor de 500 KB, pero es específico del dispositivo, en algunos dispositivos puede transferir casi todo el 1 MB. También tuve esta excepción, así que investigué un poco y escribí una publicación que podría ser una lectura interesante para las personas que tienen este problema. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

11
Si le resulta difícil rastrear exactamente qué estado está causando su bloqueo, entonces puede encontrar útil TooLargeTool .
Max Spencer

48

Si necesita investigar qué paquete está causando su bloqueo, debería considerar probar TooLargeTool .

(Encontré esto como un comentario de @Max Spencer debajo de la respuesta aceptada y fue útil en mi caso).


99
La solución más subestimada. Esta herramienta te ayuda a reducir las actividades ofensivas
Kedar Paranjape

Esta herramienta que ayudó a resolver mi problema. Fácil de instalar y usar.
Carlos

esta herramienta es para kotlin: / alguna alternativa para java?
maxwellnewage

2
@maxwellnewage: parece que la última versión (0.2.1, 0.2.0 también) actualmente no funciona en aplicaciones Java-only. Tuve que usar la versión 0.1.6 y funcionó bien entonces
heisenberg

Usando esta herramienta, detecto que estaba usando paquetes de gran tamaño en mis fragmentos. Lo que hice fue extraer el argumento del paquete y lo borré usandobundle.clear()
EJ Chathuranga

41

Esta no es una respuesta definitiva, pero puede arrojar algo de luz sobre las causas de TransactionTooLargeExceptionay ayudar a identificar el problema.

Aunque la mayoría de las respuestas se refieren a grandes cantidades de datos transferidos, veo que esta excepción se produce de manera incidental después de un desplazamiento y un zoom intensos y de abrir repetidamente un menú giratorio de ActionBar. El bloqueo ocurre al tocar la barra de acción. (esta es una aplicación de mapeo personalizada)

Los únicos datos que se transmiten parecen ser toques del "Distribuidor de entrada" a la aplicación. Creo que esto no puede alcanzar razonablemente cerca de 1 mb en el "Transaction Buffer".

Mi aplicación se ejecuta en un dispositivo de cuatro núcleos a 1.6 GHz y usa 3 hilos para levantar pesas, manteniendo un núcleo libre para el hilo de la interfaz de usuario. Además, la aplicación usa android: largeHeap, le quedan 10 mb de montón sin usar y le quedan 100 mb de espacio para hacer crecer el montón. Entonces no diría que es un problema de recursos.

El bloqueo siempre está precedido inmediatamente por estas líneas:

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 !!!

Que no necesariamente se imprimen en ese orden, pero (por lo que he comprobado) suceden en el mismo milisegundo.

Y el rastro de la pila en sí mismo, para mayor claridad, es el mismo que en la pregunta:

E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException

Profundizando en el código fuente de Android uno encuentra estas líneas:

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);

Para mí, parece que posiblemente estoy golpeando esta característica indocumentada, donde la transacción falla por otras razones que no son una transacción que es demasiado grande. Deberían haberlo nombrado TransactionTooLargeOrAnotherReasonException.

En este momento no resolví el problema, pero si encuentro algo útil, actualizaré esta respuesta.

actualización: resultó que mi código filtró algunos descriptores de archivos, cuyo número se maximiza en Linux (generalmente 1024), y esto parece haber desencadenado la excepción. Entonces fue un problema de recursos después de todo. Verifiqué esto abriendo /dev/zero1024 veces, lo que resultó en todo tipo de excepciones extrañas en acciones relacionadas con la interfaz de usuario, incluida la excepción anterior, e incluso algunos SIGSEGV. Aparentemente, la falla al abrir un archivo / socket no es algo que se maneje / informe de manera muy limpia en todo Android.


36

¡ TransactionTooLargeExceptionNos ha estado atormentando durante aproximadamente 4 meses, y finalmente hemos resuelto el problema!

Lo que estaba sucediendo es que estamos usando a FragmentStatePagerAdapteren a ViewPager. El usuario hojearía y crearía más de 100 fragmentos (es una aplicación de lectura).

Aunque administramos los fragmentos correctamente destroyItem(), en la implementación de Android FragmentStatePagerAdapterhay un error, donde se mantiene una referencia a la siguiente lista:

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();

Y cuando el Android FragmentStatePagerAdapterintente salvar el estado, llamará a la función

@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;
}

Como puede ver, incluso si administra adecuadamente los fragmentos en la FragmentStatePagerAdaptersubclase, la clase base seguirá almacenando un Fragment.SavedStatefragmento por cada fragmento creado. Esto TransactionTooLargeExceptionocurriría cuando esa matriz se volcara a ay parcelableArrayel sistema operativo no quisiera tener más de 100 elementos.

Por lo tanto, la solución para nosotros fue anular el saveState()método y no almacenar nada "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;
}

La mejor respuesta para mí. Muchas gracias
pavel

De todas las posibilidades para esto, para mí esto parecía la única opción. ¿Qué se almacena en el estado que le permitiría llegar a este tamaño? Si el estado se necesita tanto que hay un código para guardarlo, ¿cómo esto no causa otros problemas? Gracias
Kenny

Misma pregunta que @Kenny
Mr. Rabbit

1
@Override public Parcelable saveState() { Bundle bundle = (Bundle) super.saveState(); if (bundle != null) { Parcelable[] states = bundle.getParcelableArray("states"); // Subset only last 3 states if (states != null) states = Arrays.copyOfRange(states, states.length > 3 ? states.length - 3 : 0, states.length - 1); bundle.putParcelableArray("states", states); } else bundle = new Bundle(); return bundle; }
Ramy Sabry

Solo dejé solo los últimos 3 estados, verifique el código anterior.
Ramy Sabry

20

Para aquellos que decepcionaron amargamente en busca de la respuesta de por qué aparece TransactionTooLargeException, intente verificar cuánta información guarda en el estado de instancia.

En compile / targetSdkVersion <= 23 solo tenemos una advertencia interna sobre el gran tamaño del estado guardado, pero nada se bloquea:

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)

Pero en compile / targetSdkVersion> = 24 tenemos un verdadero bloqueo de RuntimeException en este caso:

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) 

¿Qué hacer?

Guarde los datos en la base de datos local y mantenga solo las identificaciones en estado de instancia que puede usar para recuperar estos datos.


¿Es posible mantenerlo como parámetro global y usarlo más tarde?
Jitendra ramoliya

@Jitendraramoliya Sí, puedes. Eso es exactamente lo que quiero decir.
Yazon2006

13

Esta excepción generalmente se produce cuando la aplicación se envía en segundo plano.

Así que decidí usar el método Fragmento de datos para eludir completamente el onSavedInstanceStaeciclo de vida. Mi solución también maneja estados de instancia complejos y libera memoria lo antes posible.

Primero, he creado un Fargment simple para almacenar los datos:

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;
    }
}

Luego, en mi Actividad principal, eludo completamente el ciclo de instancias guardadas y aplazo la responsabilidad de mi Fragmento de datos. No es necesario usar esto en los Fragmentos mismos, dado que su estado se agrega al estado de la Actividad automáticamente):

@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
}

Lo que queda es simplemente hacer estallar la instancia guardada:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(SavedInstanceFragment.getInstance(getFragmentManager()).popData());
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState( SavedInstanceFragment.getInstance( getFragmentManager() ).popData() );
}

Detalles completos: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
¿Qué sucede si la actividad se destruye cuando la aplicación está en segundo plano e intentas ponerla en primer plano?
Master Disaster

@MasterDisaster en ese caso no se guarda ningún estado porque el proceso que contiene el fragmento de instancia ha muerto.
Vaiden

Derechos, por lo que este caso solo funciona con cambios de configuración.
Desastre maestro el

Funciona siempre que se activa el sistema operativo onSavedState(), lo que sucede en muchos casos. El cambio de configuración es uno. Cambiar de aplicación e ir al fondo es otra. Y hay más.
Vaiden

1
Creo que esta solución debería ampliarse para guardar datos de diferentes fuentes. Probablemente un HashMap con etiquetas como claves y paquetes como valores ..
CoolMind

11

No hay una causa específica de este problema. Para mí, en mi clase Fragment, estaba haciendo esto:

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;
}

en lugar de esto:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);

9

Es importante comprender que el búfer de transacciones está limitado a 1 MB, independientemente de las capacidades del dispositivo o la aplicación. Este búfer se usa con todas las llamadas API que realiza y se comparte entre todas las transacciones que una aplicación está ejecutando actualmente.

Creo que también contiene algún objeto específico, como parcelas y (Parcel.obtain())demás, por lo que es importante hacer coincidir siempre cada uno obtain()con a recycle().

Este error puede ocurrir fácilmente en llamadas API que devuelven muchos datos, aunque los datos devueltos sean inferiores a 1 MB (si todavía se están ejecutando otras transacciones).

Por ejemplo, la PackageManager.getInstalledApplication()llamada devuelve una lista de todas las aplicaciones instaladas. Agregar banderas específicas permite recuperar muchos datos adicionales. Si lo hace, es probable que falle, por lo que se recomienda no recuperar ningún dato adicional y recuperarlos por aplicación.

Sin embargo, la llamada aún puede fallar, por lo que es importante rodearla con a catchy poder volver a intentarlo si es necesario.

Hasta donde yo sé, no hay solución a este problema, excepto volver a intentarlo y asegurarse de recuperar la menor información posible.


¡Gracias por la información, ese búfer se comparte entre todas las transacciones dentro de la aplicación!
Artem Mostyaev

1
Si este fuera el caso, ¿por qué? Solo estoy usando teléfonos Android 4.4 y en ningún otro lugar. Creo que es más un error en 4.4 que no puedo entender por qué.
JPM

9

Yo también obtuve esta excepción en un Samsung S3. Sospecho 2 causas raíz,

  1. tiene mapas de bits que se cargan y ocupan demasiada memoria, use la reducción de tamaño
  2. Faltan algunos elementos dibujables de las carpetas drawable-_dpi, Android los busca en dibujables y los redimensiona, haciendo que su setContentView salte repentinamente y use mucha memoria.

Use DDMS y mire su montón mientras juega su aplicación, eso le dará alguna indicación sobre qué setcontentview está creando el problema.

Copié todos los dibujos en todas las carpetas para deshacerme del problema 2.

Problema resuelto.


Las excepciones de memoria / mapa de bits generalmente se ven diferentes. Ya los he visto probar con Android 2.x - 4.xy las excepciones siempre se ven diferentes. Pero, quién sabe, quizás esto también esté relacionado pero sea específico para las versiones 4.x.
Ixx

10
Es solo una terrible excepción, con respecto a la información, ya que no da ninguna pista sobre el origen del problema.
Ixx

Creo que los pocos días han terminado, ¿cuáles son tus hallazgos?
Denny

8

Agregue esto a su actividad

@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
    super.onSaveInstanceState(oldInstanceState);
    oldInstanceState.clear();
}

Funciona para mí, espero que también te ayude


77
Creo que es la pista más dañina. ¿Por qué deberíamos borrar los datos que queremos restaurar onCreate()?
CoolMind

2
Las implicaciones de este código es que al final no guardar el estado de la instancia ...
Justin

4

Entonces, para nosotros, estábamos tratando de enviar un objeto demasiado grande a través de nuestra interfaz AIDL a un servicio remoto. El tamaño de la transacción no puede exceder 1 MB. La solicitud se divide en fragmentos separados de 512 KB y se envía uno a la vez a través de la interfaz. Una solución brutal que conozco pero bueno, es Android :(


4

Ha borrado su antiguo InstanceState del método onSaveInstanceState , y funcionará bien. Estoy usando FragmentStatePagerAdapter para mi visor, así que mantengo debajo del método Override en mi actividad principal para borrar InstanceState.

@Override
protected void onSaveInstanceState(Bundle InstanceState) {
             super.onSaveInstanceState(InstanceState);
             InstanceState.clear();
}

Encontré esta solución desde aquí android.os.TransactionTooLargeException en Nougat


Muchas gracias.
Andrain

Gracias tu respuesta, ayúdame
Jatin Patel

3

Recientemente también me encontré con un caso interesante mientras trabajaba con el proveedor de contactos de Android .

Necesitaba cargar fotos de contactos desde la base de datos de contactos internos y, de acuerdo con la arquitectura del sistema, todos estos datos se entregan mediante consultas al proveedor de contactos.

Como funciona como una aplicación separada, todos los tipos de transferencia de datos se realizan utilizando el mecanismo Binder, por lo que el búfer Binder entra en juego aquí.

Mi error principal fue que no lo hice cerca de la Cursorcon los datos blob recibido de contactos de proveedores, de manera que la memoria asignada para el proveedor aumentó y esto infla a amortiguar el aglutinante hasta que me dieron un montón de !!!FAILED BINDER TRANSACTION!!!mensajes en mi salida LogCat.

Entonces, la idea principal es que cuando trabajas con proveedores de contenido externos y recibes Cursormensajes de ellos, siempre ciérralo cuando termines de trabajar con ellos.


3

Me enfrenté al mismo problema cuando intenté enviar un mapa de bits a través de Intent y al mismo tiempo cuando sucedió, doblé la aplicación.

Cómo se describe en este artículo, ingrese la descripción del enlace aquí , sucede cuando una Actividad está en proceso de detención, lo que significa que la Actividad estaba tratando de enviar sus Paquetes de estado guardados al sistema operativo del sistema para mantenerlos seguros para su restauración más tarde (después de un cambio de configuración o procesar la muerte), pero que uno o más de los paquetes que envió eran demasiado grandes.

Lo resolví a través de un pirateo anulando onSaveInstanceState en mi Actividad:

@Override
protected void onSaveInstanceState(Bundle outState) {
    // super.onSaveInstanceState(outState);
}

y comentar llamar super. Es un truco sucio pero funciona perfectamente. El mapa de bits se envió correctamente sin bloqueos. Espero que esto ayude a alguien.


2

En mi caso, recibo TransactionTooLargeException como un bloqueo secundario después de que la biblioteca nativa se bloqueó con SIGSEGV. El bloqueo de la biblioteca nativa no se informa, por lo que solo recibo TransactionTooLargeException.


2

Obtuve esto en mi adaptador syncadapter cuando intenté insertar en masa un ContentValues ​​grande []. Decidí arreglarlo de la siguiente manera:

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));
}

2
¿Qué pasa si el otro falla? Necesita dividir más, usando un bucle. ¿Hay alguna forma de obtener el tamaño de la transacción y obtener el tamaño máximo de la transacción?
Desarrollador de Android

2

Para mí también fue FragmentStatePagerAdapter, sin embargo, la anulación saveState()no funcionó. Así es como lo arreglé:

Al llamar al FragmentStatePagerAdapterconstructor, mantenga una lista separada de fragmentos dentro de la clase y agregue un método para eliminar los fragmentos:

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...
}

Luego, en el Activity, guarde la ViewPagerposición y llame adapter.removeFragments()al onSaveInstanceState()método anulado :

private int pagerPosition;

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //save other view state here
    pagerPosition = mViewPager.getCurrentItem();
    adapter.removeFragments();
}

Por último, en el onResume()método anulado , vuelva a crear una instancia del adaptador si no lo está null. (Si es así null, Activityse abrirá por primera vez o después de que Android elimine la aplicación, lo que onCreatehará la creación del adaptador).

@Override
public void onResume() {
    super.onResume();
    if (adapter != null) {
        adapter = new PagerAdapter(frags);
        mViewPager.setAdapter(adapter);
        mViewPager.setCurrentItem(currentTabPosition);
    }
}

1

Asegúrese de no colocar datos de objeto Intent de gran tamaño. En mi caso, agregué String 500k y luego comencé otra actividad. Siempre fallaba con esta excepción. Evité compartir datos entre actividades mediante el uso de variables estáticas de actividades: no es necesario enviarlas a Intent y luego extraerlas.

Lo que tuve

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);

Esta es una muy mala idea. Usar variables estáticas de esa manera es un olor a código. Además, intente ejecutar este código con el indicador "No mantener actividades" y presione Inicio después de navegar a PageWebView. Después de volver a abrir su actividad, probablemente obtendrá un bloqueo ya que MainActivity estará muerto al 100% con todas sus variables.
Kyrylo Zapylaiev

1

Cuando estoy tratando con el WebViewen mi aplicación sucede. Creo que está relacionado con los addViewrecursos de UI. En mi aplicación agrego un código WebViewActivitycomo este a continuación y luego funciona bien:

@Override
protected void onDestroy() {
    if (mWebView != null) {
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);  
        mWebView.removeAllViews();  
        mWebView.destroy();
    }
    super.onDestroy();
}

1

Encontré la causa raíz de esto (obtuvimos "error al agregar ventana" y fuga del descriptor de archivo como dice mvds).

Hay un error en BitmapFactory.decodeFileDescriptor()Android 4.4. Solo ocurre cuando inPurgeabley inInputShareablede BitmapOptionsse establecen en true. Esto causa muchos problemas en muchos lugares interactuar con archivos.

Tenga en cuenta que el método también se llama desde MediaStore.Images.Thumbnails.getThumbnail().

Universal Image Loader se ve afectado por este problema. Picasso y Glide parecen no verse afectados. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

Esta línea de código en el método writeToParcel (Parcel dest, int flags) me ayudó a deshacerme de TransactionTooLargeException.

dest=Parcel.obtain(); 

Después de este código, solo estoy escribiendo todos los datos en el objeto de paquete, es decir, dest.writeInt (), etc.


1

Intenta usar EventBuso una ContentProvidersolución similar.

Si está en el mismo proceso (normalmente todas sus actividades lo harían), intente usarlo EventBus, porque el intercambio de datos en el proceso NO necesita un poco de búfer, por lo que no debe preocuparse porque sus datos sean demasiado grandes. (Simplemente puede usar la llamada al método para pasar datos, y EventBus oculta las cosas feas) Aquí está el detalle:

// one side
startActivity(intentNotTooLarge);
EventBus.getDefault().post(new FooEvent(theHugeData));

// the other side
@Subscribe public void handleData(FooEvent event) { /* get and handle data */ }

Si los dos lados de la intención no están en el mismo proceso, intente un poco ContentProvider.


Ver TransactionTooLargeException

La transacción de Binder falló porque era demasiado grande.

Durante una llamada a procedimiento remoto, los argumentos y el valor de retorno de la llamada se transfieren como objetos Parcel almacenados en el búfer de transacciones de Binder. Si los argumentos o el valor de retorno son demasiado grandes para caber en el búfer de transacciones, la llamada fallará y se lanzará TransactionTooLargeException.


1

Obtuve una TransactionTooLargeException de un error de Stackoverflow en una prueba de Android Espresso. Encontré el seguimiento de pila de error stackoverflow en los registros cuando quité el filtro Logcat para mi aplicación.

Supongo que Espresso causó la TransactionTooLargeException al tratar de manejar un stacktrace de excepción realmente grande.


1

Uno puede usar:

android:largeHeap="true"

en el Manifiesto de Android bajo la etiqueta de la aplicación.

¡Esto resolvió el problema en mi caso!


En mi caso (por llamar onSaveInstantStatea actividades / fragmentos y guardar listas grandes) no ayudó.
CoolMind

1
Esto se considera una mala práctica porque no se trata de la razón por la cual se guardan muchos datos en primer lugar. En dispositivos más nuevos, se bloquea la aplicación y el límite es mucho menor (256 KB). Investigue por qué está almacenando lotes primero y reduzca.
Tim Kist el

1

También estaba enfrentando este problema para los datos de mapa de bits que pasaban de una actividad a otra, pero hago una solución al hacer que mis datos sean datos estáticos y esto funciona perfecto para mí

En actividad primero:

public static Bitmap bitmap_image;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_first);
   bitmap_image=mybitmap;
}

y en segunda actividad:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
   Bitmap mybitmap=first.bitmap_image;
}

1

Esto estaba sucediendo en mi aplicación porque estaba pasando una lista de resultados de búsqueda en los argumentos de un fragmento, asignando esa lista a una propiedad del fragmento, que en realidad es una referencia a la misma ubicación en la memoria señalada por los argumentos del fragmento, y luego agregué nuevos elementos a la lista, que también cambiaron el tamaño de los argumentos del fragmento. Cuando se suspende la actividad, la clase de fragmento base intenta guardar los argumentos del fragmento en onSaveInstanceState, que se bloquea si los argumentos son mayores de 1 MB. Por ejemplo:

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);
}

La solución más fácil en este caso fue asignar una copia de la lista a la propiedad del fragmento en lugar de asignar una referencia:

@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"));
    }
}

Una solución aún mejor sería no pasar tantos datos en los argumentos.

Probablemente nunca hubiera encontrado esto sin la ayuda de esta respuesta y TooLargeTool .


1

También he vivido TransactionTooLargeException. En primer lugar, he trabajado para entender dónde ocurre. Sé la razón por la que ocurre. Todos lo sabemos por su gran contenido. Mi problema era así y lo resolví. Quizás esta solución pueda ser útil para cualquiera. Tengo una aplicación que obtiene contenido de la API. Recibo el resultado de la API en la primera pantalla y lo envío a la segunda pantalla. Puedo enviar este contenido a la segunda pantalla con éxito. Después de la segunda pantalla, si quiero ir a la tercera pantalla, se produce esta excepción. Cada una de mi pantalla se crea a partir de Fragment. Me di cuenta de eso cuando salgo de la segunda pantalla. Guarda su contenido del paquete. Si este contenido es demasiado grande, se produce esta excepción. Mi solución es que después de obtener el contenido del paquete, lo borro.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }

Si bien esto es correcto (lástima, entendí lo mismo después de un año), ¿cómo funcionará el fragmento si se recrea (rotación de pantalla)?
CoolMind

0

Una solución sería que la aplicación escriba ArrayList (o cualquier objeto que esté causando el problema) en el sistema de archivos, luego pase una referencia a ese archivo (por ejemplo, nombre de archivo / ruta) a través del Intent al IntentService y luego deje el IntentService recuperar el contenido del archivo y convertirlo nuevamente en una ArrayList.

Cuando IntentService haya terminado con el archivo, debe eliminarlo o pasar la instrucción a la aplicación a través de una transmisión local para eliminar el archivo que creó (devolviendo la misma referencia de archivo que se le proporcionó).

Para obtener más información, vea mi respuesta a este problema relacionado .


0

Como intenciones, los proveedores de contenido, Messenger, todos los servicios del sistema como teléfono, vibrador, etc. utilizan el proveedor de infraestructura IPC de Binder. Además, las devoluciones de llamada del ciclo de vida de la actividad también utilizan esta infraestructura.

1 MB es el límite general de todas las transacciones de carpeta ejecutadas en el sistema en un momento particular.

En caso de que ocurran muchas transacciones cuando se envía la intención, puede fallar aunque los datos adicionales no sean grandes. http://codetheory.in/an-overview-of-android-binder-framework/


0

Con tantos lugares donde TransactionTooLargeException puede ocurrir , aquí hay uno nuevo para Android 8, un bloqueo cuando alguien simplemente comienza a escribir en EditText si el contenido es demasiado grande.

Está relacionado con AutoFillManager (nuevo en API 26) y el siguiente código en StartSessionLocked():

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

Si entiendo correctamente, esto llama al servicio de autocompletar, pasando el AutofillManagerClient dentro del cuaderno. Y cuando EditText tiene mucho contenido, parece causar el TTLE.

Algunas cosas pueden mitigarlo (o hice lo que estaba probando de todos modos): agregue android:importantForAutofill="noExcludeDescendants"la declaración de diseño xml de EditText. O en código:

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

Una segunda solución horrible y terrible también podría ser anular los métodos performClick()y onWindowFocusChanged()para detectar el error en una subclase TextEdit. Pero no creo que sea sabio realmente ...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.