Cosa fare su TransactionTooLargeException


239

Ho un TransactionTooLargeException. Non riproducibile Nei documenti dice

La transazione Binder non è riuscita perché era troppo grande.

Durante una chiamata di procedura remota, gli argomenti e il valore di ritorno della chiamata vengono trasferiti come oggetti Parcel memorizzati nel buffer delle transazioni del Raccoglitore. Se gli argomenti o il valore restituito sono troppo grandi per adattarsi al buffer delle transazioni, la chiamata fallirà e verrà generata TransactionTooLargeException.

...

Esistono due possibili risultati quando una chiamata di procedura remota genera TransactionTooLargeException. Il client non è stato in grado di inviare la sua richiesta al servizio (molto probabilmente se gli argomenti erano troppo grandi per adattarsi al buffer delle transazioni) o il servizio non è stato in grado di inviare la sua risposta al client (molto probabilmente se il valore di ritorno era troppo grande per adattarsi al buffer delle transazioni).

...

Quindi da qualche parte sto passando o ricevendo argomenti che superano qualche limite sconosciuto. Dove?

Lo stacktrace non mostra nulla di utile:

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)

Sembra essere in relazione con le opinioni? In che modo è correlato alla chiamata di procedura remota?

Forse importante: versione Android: 4.0.3, dispositivo: HTC One X


No. Ma non l'ho preso di nuovo. Avere un tracker errori nell'app live e ottenerlo solo una volta in circa 3 settimane. Almeno non sembra accadere frequentemente. Forse vale la pena aprire un problema su Android anche se ...
Ixx

Non ho una risposta, ma questo fa sì che il mio Galaxy S2 si reimposti in modo affidabile.
Timmmm,

Ho appena avuto questo accadere in una delle mie applicazioni oggi. Questo è successo anche solo una volta e con un Galaxy S3. È interessante che questo appaia solo con i dispositivi più potenti.
danwms,

quell'eccezione è stata aggiunta nell'API 15, developer.android.com/reference/android/os/… E l'ho riprodotta in MapView mentre scorrevo la mappa. fino a quando il gc scrisse che non avevo più memoria (mi ci vollero un paio di minuti)
meh

Il buffer delle transazioni è limitato a 1 MB su tutti i dispositivi e questo buffer annulla ogni transazione. Quindi, più potente è il dispositivo, più transazioni può eseguire simultaneamente, tutte consumando lo stesso buffer da 1 MB. Detto questo, la tua risposta non è una risposta ma un commento.
3c71,

Risposte:


158

Ho riscontrato questo problema e ho scoperto che quando viene scambiata un'enorme quantità di dati tra un servizio e un'applicazione (ciò comporta il trasferimento di molte anteprime). In realtà la dimensione dei dati era di circa 500 kb e la dimensione del buffer delle transazioni IPC è impostata su 1024 KB. Non sono sicuro del motivo per cui ha superato il buffer delle transazioni.

Questo può accadere anche quando si passano molti dati attraverso extra intenti

Quando ricevi questa eccezione nella tua applicazione, ti preghiamo di analizzare il tuo codice.

  1. Stai scambiando molti dati tra i tuoi servizi e l'applicazione?
  2. Utilizzando gli intenti per condividere enormi dati (ad esempio, l'utente seleziona un numero enorme di file dalla condivisione della condivisione della galleria, gli URI dei file selezionati verranno trasferiti usando gli intenti)
  3. ricevere file bitmap dal servizio
  4. in attesa che Android risponda con enormi quantità di dati (ad esempio, getInstalledApplications () quando l'utente ha installato molte applicazioni)
  5. usando applyBatch () con molte operazioni in sospeso

Come gestire quando si ottiene questa eccezione

Se possibile, dividere l'operazione grande in piccoli blocchi, ad esempio, invece di chiamare applyBatch () con 1000 operazioni, chiamarla con 100 ciascuna.

Non scambiare dati enormi (> 1 MB) tra servizi e applicazioni

Non so come farlo, ma, Non eseguire query su Android, che può restituire enormi dati :-)


1
Supponiamo che sto controllando il mio .apk è installato o no? al momento dell'installazione ... Ricevo la stessa eccezione durante il controllo del mio pacchetto com.test.installedornot. La mia dimensione .apk è superiore a 9 MB, in tal caso come gestirò questa eccezione?
DJhon

15
Ricevo questa eccezione esattamente durante una chiamata a getInstalledApplications. Cosa si può fare per risolvere questo?
Stan,

1
@Stan Questo api è comune e ampiamente utilizzato nelle app Android. Questa eccezione mi preoccupa davvero in qualche modo quando uso questa API.
peacepassion,

7
Posso confermare le tue conclusioni sul limite di essere da qualche parte intorno a 500 KB, ma è specifico del dispositivo, su alcuni dispositivi puoi trasferire quasi l'intero 1 MB. Ho avuto anche questa eccezione, quindi ho fatto qualche indagine e ho scritto un post che potrebbe essere interessante da leggere per le persone che hanno questo problema. nemanjakovacevic.net/blog/eng/2015 2015
Nemanja Kovacevic

11
Se trovi difficile rintracciare esattamente quale stato sta causando il tuo arresto, potresti trovare utile TooLargeTool .
Max Spencer,

48

Se devi investigare quale Parcel sta causando il tuo crash, dovresti provare a provare TooLargeTool .

(Ho trovato questo come un commento di @Max Spencer nella risposta accettata ed è stato utile nel mio caso.)


9
La soluzione più sottovalutata. Questo strumento ti aiuta a limitare le attività offensive
Kedar Paranjape,

Questo strumento che mi ha aiutato a risolvere il mio problema. Facile da installare e da usare.
Carlos,

questo strumento è per kotlin: / qualche alternativa per java?
maxwellnewage,

2
@maxwellnewage: sembra che l'ultima versione (anche 0.2.1, 0.2.0) al momento non funzioni nelle sole applicazioni Java. Ho dovuto usare la versione 0.1.6 e ha funzionato bene allora
heisenberg

Usando questo strumento rilevo che stavo usando enormi dimensioni di fasci nei miei frammenti. Quello che ho fatto è stato estrarre l'argomento dal pacchetto e cancellarlo usandobundle.clear()
EJ Chathuranga

41

Questa non è una risposta definitiva, ma può far luce sulle cause di a TransactionTooLargeExceptione aiutare a individuare il problema.

Sebbene la maggior parte delle risposte si riferisca a grandi quantità di dati trasferiti, vedo che questa eccezione viene generata accidentalmente dopo uno scorrimento e uno zoom pesanti e l'apertura ripetuta di un menu di selezione di ActionBar. L'incidente si verifica toccando la barra delle azioni. (questa è un'app di mappatura personalizzata)

Gli unici dati trasmessi sembrano essere tocchi da "Input Dispatcher" all'app. Penso che questo non possa ragionevolmente ammontare a quasi 1 mb nel "Transaction Buffer".

La mia app è in esecuzione su un dispositivo quad core da 1,6 GHz e utilizza 3 thread per il heavylifting, mantenendo un core libero per il thread dell'interfaccia utente. Inoltre, l'app utilizza Android: largeHeap, ha 10 mb di heap inutilizzati rimasti e 100 mb di spazio lasciato per far crescere l'heap. Quindi non direi che è un problema di risorse.

L'arresto anomalo è sempre immediatamente preceduto da queste righe:

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

Che non sono necessariamente stampati in quell'ordine, ma (per quanto ho verificato) avvengono nello stesso millisecondo.

E lo stack trace stesso, per chiarezza, è lo stesso della domanda:

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

Scavando nel codice sorgente di Android si trovano queste righe:

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

Per me sembra che stia probabilmente colpendo questa funzione non documentata, in cui la transazione fallisce per motivi diversi da una transazione troppo grande. Avrebbero dovuto chiamarlo TransactionTooLargeOrAnotherReasonException.

Al momento non ho risolto il problema, ma se trovo qualcosa di utile aggiornerò questa risposta.

aggiornamento: si è scoperto che il mio codice trapelava alcuni descrittori di file, il cui numero è massimizzato in Linux (in genere 1024) e questo sembra aver innescato l'eccezione. Quindi, dopo tutto, era un problema di risorse. L'ho verificato aprendo /dev/zero1024 volte, il che ha comportato tutti i tipi di strane eccezioni nelle azioni relative all'interfaccia utente, inclusa l'eccezione sopra, e persino alcuni SIGSEGV. Apparentemente la mancata apertura di un file / socket non è qualcosa che viene gestito / segnalato in modo molto pulito su Android.


36

La TransactionTooLargeExceptionc'è stata affligge per circa 4 mesi, e abbiamo finalmente risolto il problema!

Quello che stava succedendo era che stiamo usando a FragmentStatePagerAdapterin ViewPager. L'utente dovrebbe sfogliare e creare oltre 100 frammenti (è un'applicazione di lettura).

Sebbene gestiamo correttamente i frammenti destroyItem(), nell'implementazione di Androids FragmentStatePagerAdapterc'è un bug, in cui mantiene un riferimento al seguente elenco:

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

E quando Android FragmentStatePagerAdaptertenta di salvare lo stato, chiamerà la funzione

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

Come puoi vedere, anche se gestisci correttamente i frammenti nella FragmentStatePagerAdaptersottoclasse, la classe base memorizzerà comunque un Fragment.SavedStateper ogni singolo frammento mai creato. L' TransactionTooLargeExceptionevento si sarebbe verificato quando quell'array era stato scaricato su un parcelableArraye il sistema operativo non gli sarebbe piaciuto più di 100 elementi.

Pertanto la soluzione per noi era quella di sovrascrivere il saveState()metodo e non archiviare nulla per "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 migliore risposta per me. Grazie mille
Pavel,

Di tutte le possibilità per questo, per me questa sembrava l'unica opzione. Cosa viene memorizzato nello stato che gli consentirebbe di raggiungere queste dimensioni? Se lo stato è necessario così tanto che esiste un codice per salvarlo, in che modo ciò non causa altri problemi? Grazie
Kenny,

Stessa domanda di @Kenny
Mr. Rabbit l'

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,

Ho appena lasciato solo gli ultimi 3 stati, controlla il codice sopra.
Ramy Sabry,

20

Per coloro che hanno amaramente deluso la ricerca della risposta sul perché appare TransactionTooLargeException, prova a controllare quante informazioni salvi nello stato dell'istanza.

Su compilazione / targetSdkVersion <= 23 abbiamo solo un avviso interno sulle grandi dimensioni dello stato salvato, ma nulla è andato in crash:

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)

Ma su compilazione / targetSdkVersion> = 24 abbiamo un vero arresto RuntimeException in questo 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) 

Cosa fare?

Salvare i dati nel database locale e mantenere solo gli ID nello stato di istanza che è possibile utilizzare per recuperare questi dati.


è possibile mantenerlo come parametro globale e utilizzarlo in seguito?
Jitendra ramoliya,

@Jitendraramoliya Sì, puoi. Questo è esattamente ciò che intendo.
Yazon2006,

13

Questa eccezione viene in genere generata quando l'app viene inviata in background.

Quindi ho deciso di utilizzare il metodo Data Fragment per aggirare completamente il onSavedInstanceStaeciclo di vita. La mia soluzione gestisce anche stati di istanza complessi e libera la memoria il più presto possibile.

Per prima cosa ho creato un semplice Fargment per archiviare i dati:

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

Quindi sulla mia attività principale eluso completamente il ciclo dell'istanza salvata e rinvio la responsabilità al mio frammento di dati. Non è necessario utilizzarlo sui frammenti stessi, poiché il loro stato viene aggiunto automaticamente allo stato dell'attività):

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

Ciò che resta è semplicemente far apparire l'istanza salvata:

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

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

Dettagli completi: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
Cosa succede se l'attività viene distrutta quando l'app è in background e si tenta di mettere in primo piano l'attività?
Master Disaster,

@MasterDisaster in quel caso nessuno stato viene salvato perché il processo che contiene il frammento di istanza è morto.
Vaiden,

Diritti, quindi questo caso funziona solo con la modifica della configurazione.
Master Disaster,

Funziona ogni volta che si attiva il sistema operativo onSavedState(), cosa che accade in molti casi. La modifica della configurazione è una. Cambiare app e passare allo sfondo sono un'altra cosa. E ce ne sono altri.
Vaiden,

1
Penso che questa soluzione dovrebbe essere espansa per salvare i dati da diverse fonti. Probabilmente una HashMap con tag come chiavi e bundle come valori ..
CoolMind

11

Non esiste una causa specifica di questo problema. Per me, nella mia classe Fragment stavo facendo questo:

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

Invece di questo:

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

9

È importante comprendere che il buffer delle transazioni è limitato a 1 MB, indipendentemente dalle funzionalità del dispositivo o dall'app. Questo buffer viene utilizzato con tutte le chiamate API effettuate ed è condiviso tra tutte le transazioni che un'app è attualmente in esecuzione.

Credo che contenga anche alcuni oggetti specifici come pacchi e simili (Parcel.obtain()), quindi è importante abbinare sempre ciascuno obtain()con un recycle().

Questo errore può verificarsi facilmente nelle chiamate API che restituiscono molti dati, anche se i dati restituiti sono inferiori a 1 MB (se sono ancora in esecuzione altre transazioni).

Ad esempio, la PackageManager.getInstalledApplication()chiamata restituisce un elenco di tutte le app installate. L'aggiunta di flag specifici consente di recuperare molti dati extra. È probabile che ciò non vada a buon fine, pertanto si consiglia di non recuperare alcun dato aggiuntivo e di recuperarli in base all'app.

Tuttavia, la chiamata potrebbe comunque non riuscire, quindi è importante circondarla con un catche poter riprovare se necessario.

Per quanto ne so, non è possibile aggirare il problema se non riprovare e assicurarsi di recuperare il minor numero di informazioni possibile.


Grazie per le informazioni, quel buffer è condiviso tra tutte le transazioni all'interno dell'applicazione!
Artem Mostyaev,

1
Se questo fosse il caso, allora perché ottengo solo telefoni Android 4.4 e non altrove. Penso che sia più un bug nel 4.4 che non riesco a capire perché.
JPM,

9

Anch'io ho questa eccezione su un Samsung S3. Sospetto che 2 cause alla radice,

  1. hai bitmap che caricano e occupano troppa memoria, usano il ridimensionamento
  2. Mancano alcuni drawable dalle cartelle drawable-_dpi, Android li cerca in drawable e li ridimensiona, facendo saltare improvvisamente setContentView e usando molta memoria.

Usa DDMS e osserva il tuo heap mentre giochi la tua app, che ti darà qualche indicazione su quale setcontentview sta creando il problema.

Ho copiato tutti i disegni in tutte le cartelle per eliminare il problema 2.

Il problema è stato risolto


Le eccezioni di memoria / bitmap di solito sembrano diverse. Ne ho già visti molti testare con Android 2.x - 4.x e le eccezioni sembrano sempre diverse. Ma, chissà, forse questo è anche correlato ma specifico per le versioni 4.x.
Ixx,

10
È solo una terribile eccezione, per quanto riguarda le informazioni, dal momento che non fornisce alcun indizio su da dove provenga il problema.
Ixx,

Penso che i pochi giorni siano finiti, quali sono le tue scoperte?
Denny,

8

Aggiungi questo alla tua attività

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

Funziona per me spero anche che ti aiuterà


7
Penso che sia il suggerimento più dannoso. Perché dovremmo cancellare i dati che vogliamo ripristinare onCreate()?
CoolMind

2
Le implicazioni di questo codice sono che NON si salva il proprio stato di istanza ...
Giustino,

4

Quindi per noi cercavamo di inviare un oggetto troppo grande attraverso la nostra interfaccia AIDL a un servizio remoto. La dimensione della transazione non può superare 1 MB. La richiesta viene suddivisa in blocchi separati di 512 KB e inviata una alla volta tramite l'interfaccia. Una soluzione brutale che conosco ma hey - è Android :(


4

Hai cancellato il tuo vecchio InstanceState dal metodo onSaveInstanceState e funzionerà bene. Sto usando FragmentStatePagerAdapter per il mio viewpager, quindi tengo sotto il metodo Override nell'attività del mio genitore per cancellare InstanceState.

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

Ho trovato questa soluzione da qui android.os.TransactionTooLargeException su Nougat


Grazie mille.
Andrain,

Grazie la tua risposta aiutami
Jatin Patel,

3

Di recente ho anche riscontrato un caso interessante mentre lavoravo con il provider di contatti di Android .

Avevo bisogno di caricare le foto dei contatti dal database dei contatti interni e secondo l'architettura del sistema tutti questi dati vengono consegnati tramite query al fornitore di contatti.

Poiché funziona come un'applicazione separata, tutti i tipi di trasferimento dei dati vengono eseguiti utilizzando il meccanismo Binder e quindi il buffer Binder entra in gioco qui.

Il mio errore principale è stato quello di non aver chiuso i Cursordati con BLOB ottenuti dal provider di contatti, in modo che la memoria allocata per il provider aumentasse e questo aumentasse il buffer di Binder fino a quando non avessi ricevuto tonnellate di !!!FAILED BINDER TRANSACTION!!!messaggi nell'output di LogCat.

Quindi l'idea principale è che quando lavori con fornitori di contenuti esterni e ne Cursorricevi, chiudi sempre quando finisci di lavorare con loro.


3

Ho riscontrato lo stesso problema quando ho provato a inviare bitmap tramite Intent e allo stesso tempo, quando succede, ho piegato l'applicazione.

Il modo in cui è stato descritto in questo articolo immette la descrizione del collegamento qui accade quando un'attività è in fase di interruzione, ciò significa che l'attività stava cercando di inviare i pacchetti di stato salvati al sistema operativo per conservarli in modo sicuro per il ripristino in seguito (dopo una modifica della configurazione o elaborare la morte) ma che uno o più dei Bundle inviati erano troppo grandi.

L'ho risolto tramite hack sovrascrivendo onSaveInstanceState nella mia attività:

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

e commento chiama super. È un trucco sporco ma funziona perfettamente. Bitmap è stata inviata correttamente senza arresti anomali. Spero che questo possa aiutare qualcuno.


2

Nel mio caso ottengo TransactionTooLargeException come arresto secondario dopo l'arresto anomalo della libreria nativa con SIGSEGV. L'arresto anomalo della libreria nativa non viene segnalato, quindi ricevo solo TransactionTooLargeException.


2

Ho trovato questo nel mio syncadapter quando provavo a bulkInserire un ContentValues ​​di grandi dimensioni []. Ho deciso di risolverlo come segue:

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
E se l'altro fallisse? Devi fare più divisione, usando un loop. C'è forse un modo per ottenere la dimensione della transazione e ottenere la dimensione massima della transazione?
sviluppatore Android

2

Per me è stato anche il FragmentStatePagerAdapter, per quanto prevalente saveState()non ha funzionato. Ecco come l'ho risolto:

Quando chiami il FragmentStatePagerAdaptercostruttore, mantieni un elenco separato di frammenti all'interno della classe e aggiungi un metodo per rimuovere i frammenti:

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

Quindi in Activity, salva la ViewPagerposizione e chiama adapter.removeFragments()nel onSaveInstanceState()metodo ignorato :

private int pagerPosition;

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

Infine, nel onResume()metodo ignorato , ri-istanziare l'adattatore se non lo è null. (Se lo è null, allora Activityviene aperto per la prima volta o dopo che l'app è stata uccisa da Android, in cui onCreateverrà eseguita la creazione dell'adattatore.)

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

1

Assicurati di non inserire dati di oggetti Intent di grandi dimensioni. Nel mio caso stavo aggiungendo una stringa di 500k e quindi iniziando un'altra attività. Ha sempre fallito con questa eccezione. Ho evitato di condividere i dati tra le attività utilizzando variabili statiche di attività: non è necessario inviarle a Intent e quindi estrarle.

Quello che avevo:

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

Questa è una pessima idea. L'uso di variabili statiche in questo modo è un odore di codice. Inoltre, prova a eseguire questo codice con il flag "Non conservare le attività" e premi Home dopo essere passato a PageWebView. Dopo aver riaperto la tua attività, probabilmente otterrai un arresto poiché MainActivity sarà morto al 100% con tutte le sue variabili.
Kyrylo Zapylaiev,

1

Quando ho a che fare con la WebViewmia app succede. Penso che sia correlato alle addViewrisorse dell'interfaccia utente. Nella mia app aggiungo un po 'di codice in WebViewActivityquesto modo sotto quindi funziona bene:

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

1

Ho trovato la causa principale di questo (abbiamo ottenuto sia "l'aggiunta della finestra non riuscita" sia la perdita del descrittore di file come dice mvds).

È presente un bug in BitmapFactory.decodeFileDescriptor()Android 4.4. Si verifica solo quando inPurgeablee inInputShareableof BitmapOptionssono impostati su true. Ciò causa molti problemi in molti luoghi interagire con i file.

Si noti che il metodo viene anche chiamato da MediaStore.Images.Thumbnails.getThumbnail().

Universal Image Loader è interessato da questo problema. Picasso e Glide sembrano non essere interessati. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

Questa riga di codice nel metodo writeToParcel (Parcel dest, int flags) mi ha aiutato a sbarazzarmi di TransactionTooLargeException.

dest=Parcel.obtain(); 

Dopo questo codice sto solo scrivendo tutti i dati sull'oggetto pacco cioè dest.writeInt () ecc.


1

Prova a usare EventBusoContentProvider soluzione simile.

Se sei nello stesso processo (normalmente tutte le tue attività sarebbero), prova a usarlo EventBus, perché nel processo lo scambio di dati NON ha bisogno di un buffer, quindi non devi preoccuparti che i tuoi dati siano troppo grandi. (Puoi semplicemente usare la chiamata di metodo per passare effettivamente i dati ed EventBus nasconde le cose brutte) Ecco il dettaglio:

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

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

Se i due lati di Intent non sono nello stesso processo, prova un po ' ContentProvider.


Vedi TransactionTooLargeException

La transazione Binder non è riuscita perché era troppo grande.

Durante una chiamata di procedura remota, gli argomenti e il valore di ritorno della chiamata vengono trasferiti come oggetti Parcel memorizzati nel buffer delle transazioni del Raccoglitore. Se gli argomenti o il valore restituito sono troppo grandi per adattarsi al buffer delle transazioni, la chiamata fallirà e verrà generata TransactionTooLargeException.


1

Ho ricevuto TransactionTooLargeException da un errore StackOverflow in un test Android Espresso. Ho trovato la traccia dello stack dell'errore stackoverflow nei log quando ho rimosso il filtro Logcat per la mia app.

Immagino che Espresso abbia causato TransactionTooLargeException quando ha provato a gestire uno stacktrace di eccezioni davvero grande.


1

Si può usare:

android:largeHeap="true"

in Android Manifest sotto il tag dell'applicazione.

Questo ha risolto il problema nel mio caso!


Nel mio caso (a causa della chiamata onSaveInstantStatein attività / frammenti e il salvataggio di elenchi di grandi dimensioni) non ha aiutato.
CoolMind

1
Questa è considerata una cattiva pratica perché non hai a che fare con il motivo per cui in primo luogo vengono salvati molti dati. Sui dispositivi più recenti, blocca l'app e il limite è molto più piccolo (256 KB). Scopri perché stai memorizzando prima i lotti e ridurli.
Tim Kist

1

Inoltre stavo affrontando questo problema per i dati Bitmap che passano da un'attività all'altra, ma faccio una soluzione rendendo i miei dati come dati statici e questo funziona perfettamente per me

Prima in attività:

public static Bitmap bitmap_image;

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

e nella seconda attività:

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

1

Questo stava accadendo nella mia app perché stavo passando un elenco di risultati di ricerca negli argomenti di un frammento, assegnando tale elenco a una proprietà del frammento - che in realtà è un riferimento alla stessa posizione in memoria indicata dagli argomenti del frammento - quindi aggiungendo nuovi elementi all'elenco, che ha anche cambiato la dimensione degli argomenti del frammento. Quando l'attività viene sospesa, la classe di frammento di base tenta di salvare gli argomenti del frammento in onSaveInstanceState, che si arresta in modo anomalo se gli argomenti sono più grandi di 1 MB. Per esempio:

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 soluzione più semplice in questo caso era assegnare una copia dell'elenco alla proprietà del frammento invece di assegnare un riferimento:

@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 soluzione ancora migliore sarebbe quella di non trasferire così tanti dati negli argomenti.

Probabilmente non l'avrei mai trovato senza l'aiuto di questa risposta e TooLargeTool .


1

Ho anche vissuto TransactionTooLargeException. In primo luogo ho lavorato per capire dove si verifica. Conosco il motivo per cui si verifica. Ognuno di noi lo sa a causa del grande contenuto. Il mio problema era così e ho risolto. Forse questa soluzione può essere utile per chiunque. Ho un'app che ottiene contenuti da api. Ricevo il risultato dall'API nella prima schermata e lo invio alla seconda schermata. Posso inviare correttamente questi contenuti alla seconda schermata. Dopo la seconda schermata, se voglio passare alla terza schermata, si verifica questa eccezione. Ognuno dei miei schermi è creato da Fragment. L'ho notato quando esco dalla seconda schermata. Salva il contenuto del pacchetto. se questo contenuto è troppo grande, si verifica questa eccezione. La mia soluzione è che dopo aver ricevuto il contenuto dal bundle l'ho cancellato.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

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

Mentre questo è giusto (peccato per me, ho capito lo stesso dopo un anno), come farà il frammento se si ricrea (rotazione dello schermo)?
CoolMind

0

Una soluzione potrebbe essere l'app per scrivere ArrayList (o qualunque oggetto stia causando il problema) nel file system, quindi passare un riferimento a quel file (ad esempio, nome file / percorso) tramite Intent a IntentService e quindi lasciare che IntentService recuperare il contenuto del file e convertirlo nuovamente in un ArrayList.

Quando IntentService ha terminato il file, dovrebbe eliminarlo o passare nuovamente le istruzioni all'app tramite una trasmissione locale per eliminare il file che ha creato (restituendo lo stesso riferimento al file che gli è stato fornito).

Per maggiori informazioni vedi la mia risposta a questo problema correlato .


0

Come Intenti, Content Provider, Messenger, tutti i servizi di sistema come Telefono, Vibratore ecc. Utilizzano il provider di infrastrutture IPC di Binder. Inoltre, i callback del ciclo di vita delle attività utilizzano questa infrastruttura.

1 MB è il limite complessivo di tutte le transazioni di binder eseguite nel sistema in un determinato momento.

Nel caso in cui si verifichino molte transazioni quando viene inviato l'intento, potrebbe non riuscire anche se i dati extra non sono di grandi dimensioni. http://codetheory.in/an-overview-of-android-binder-framework/


0

Con così tanti posti in cui TransactionTooLargeException può accadere - ecco un'altra novità di Android 8 - un arresto anomalo quando qualcuno inizia a digitare in un EditText se il contenuto è troppo grande.

È correlato a AutoFillManager (nuovo in API 26) e al seguente codice in StartSessionLocked():

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

Se ho capito bene, questo chiama il servizio di compilazione automatica, passando AutofillManagerClient all'interno del raccoglitore. E quando EditText ha molti contenuti, sembra causare il TTLE.

Alcune cose potrebbero mitigarlo (o fare come stavo testando comunque): Aggiungi android:importantForAutofill="noExcludeDescendants"nella dichiarazione di layout xml di EditText. O nel codice:

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 seconda soluzione terribile, terribile, potrebbe anche essere quella di sovrascrivere i metodi performClick()e per rilevare onWindowFocusChanged()l'errore in una stessa sottoclasse TextEdit. Ma non credo sia davvero saggio ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.