Was ist mit TransactionTooLargeException zu tun?

239

Ich habe eine TransactionTooLargeException. Nicht reproduzierbar. In den Dokumenten steht

Die Binder-Transaktion ist fehlgeschlagen, weil sie zu groß war.

Während eines Remoteprozeduraufrufs werden die Argumente und der Rückgabewert des Aufrufs als Paketobjekte übertragen, die im Binder-Transaktionspuffer gespeichert sind. Wenn die Argumente oder der Rückgabewert zu groß sind, um in den Transaktionspuffer zu passen, schlägt der Aufruf fehl und TransactionTooLargeException wird ausgelöst.

...

Es gibt zwei mögliche Ergebnisse, wenn ein Remoteprozeduraufruf eine TransactionTooLargeException auslöst. Entweder konnte der Client seine Anforderung nicht an den Dienst senden (höchstwahrscheinlich, wenn die Argumente zu groß waren, um in den Transaktionspuffer zu passen), oder der Dienst konnte seine Antwort nicht an den Client zurücksenden (höchstwahrscheinlich, wenn der Rückgabewert war zu groß, um in den Transaktionspuffer zu passen).

...

Irgendwo übergebe oder erhalte ich Argumente, die eine unbekannte Grenze überschreiten. Wo?

Die Stapelverfolgung zeigt nichts Nützliches:

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)

Es scheint mit Ansichten verbunden zu sein? Wie hängt dies mit dem Remoteprozeduraufruf zusammen?

Möglicherweise wichtig: Android-Version: 4.0.3, Gerät: HTC One X.

Ixx
quelle
Nein, aber ich habe es nicht wieder verstanden. Habe einen Fehler-Tracker in der Live-App und habe ihn nur dieses eine Mal in ca. 3 Wochen erhalten. Zumindest scheint es nicht häufig zu passieren. Vielleicht lohnt es sich, ein Problem bei Android zu eröffnen, obwohl ...
Ixx
Ich habe keine Antwort, aber dies führt zuverlässig dazu, dass mein Galaxy S2 hart zurückgesetzt wird.
Timmmm
Ich habe dies gerade in einer meiner Anwendungen heute auftreten lassen. Dies ist auch nur einmal und mit einem Galaxy S3 passiert. Es ist interessant, dass dies nur bei den leistungsstärkeren Geräten zu verstehen scheint.
Danwms
Diese Ausnahme wurde in API 15, developer.android.com/reference/android/os/… hinzugefügt. Und ich habe sie in der MapView reproduziert, während ich durch die Karte gescrollt habe. bis der gc schrieb, dass ich kein Gedächtnis mehr habe (es dauerte ein paar Minuten)
meh
Der Transaktionspuffer ist auf allen Geräten auf 1 MB begrenzt, und dieser Puffer veraltet jede Transaktion. Je leistungsfähiger das Gerät ist, desto mehr Transaktionen können gleichzeitig ausgeführt werden, wobei alle denselben 1-MB-Puffer verbrauchen. Ihre Antwort ist jedoch keine Antwort, sondern ein Kommentar.
3c71

Antworten:

158

Ich bin auf dieses Problem gestoßen und habe festgestellt, dass beim Austausch einer großen Datenmenge zwischen einem Dienst und einer Anwendung viele Miniaturansichten übertragen werden. Tatsächlich betrug die Datengröße etwa 500 KB, und die IPC-Transaktionspuffergröße ist auf 1024 KB festgelegt. Ich bin nicht sicher, warum es den Transaktionspuffer überschritten hat.

Dies kann auch auftreten, wenn Sie viele Daten durch Absichts-Extras übergeben

Wenn Sie diese Ausnahme in Ihrer Anwendung erhalten, analysieren Sie bitte Ihren Code.

  1. Tauschen Sie viele Daten zwischen Ihren Diensten und Ihrer Anwendung aus?
  2. Verwenden von Absichten zum Teilen großer Datenmengen (z. B. wählt der Benutzer eine große Anzahl von Dateien aus der Pressefreigabe für die Galeriefreigabe aus, die URIs der ausgewählten Dateien werden mit Absichten übertragen).
  3. Empfangen von Bitmap-Dateien vom Dienst
  4. Warten auf die Antwort von Android mit riesigen Datenmengen (z. B. getInstalledApplications (), wenn der Benutzer viele Anwendungen installiert hat)
  5. Verwenden von applyBatch () mit vielen ausstehenden Operationen

Vorgehensweise, wenn Sie diese Ausnahme erhalten

Wenn möglich, teilen Sie die große Operation in kleine Blöcke auf. Rufen Sie beispielsweise anstelle von applyBatch () mit 1000 Operationen jeweils 100 auf.

Tauschen Sie keine großen Datenmengen (> 1 MB) zwischen Diensten und Anwendungen aus

Ich weiß nicht, wie ich das machen soll, aber frage Android nicht ab, da es riesige Datenmengen zurückgeben kann :-)

Durairaj Packirisamy
quelle
1
Angenommen, ich überprüfe, ob meine .apk installiert ist oder nicht? Zum Zeitpunkt der Installation ... Ich erhalte die gleiche Ausnahme, wenn ich mein Paket überprüfe. com.test.installedornot.Meine .apk-Größe beträgt mehr als 9 MB. In diesem Fall, wie werde ich diese Ausnahme verwalten?
DJhon
15
Ich bekomme diese Ausnahme genau während eines Anrufs bei getInstalledApplications. Was kann getan werden, um dies zu lösen?
Stan
1
@Stan Diese API ist weit verbreitet und wird häufig in der Android-App verwendet. Diese Ausnahme macht mir wirklich Sorgen, wenn ich diese API verwende.
Friedensleidenschaft
7
Ich kann Ihre Schlussfolgerungen bezüglich eines Limits von etwa 500 KB bestätigen, aber es ist gerätespezifisch. Auf einigen Geräten können Sie fast die gesamten 1 MB übertragen. Ich hatte auch diese Ausnahme, also habe ich einige Nachforschungen angestellt und einen Beitrag geschrieben, der für Leute mit diesem Problem interessant sein könnte. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
11
Wenn Sie Schwierigkeiten haben , genau zu ermitteln, welcher Status Ihren Absturz verursacht, ist TooLargeTool möglicherweise hilfreich.
Max Spencer
48

Wenn Sie untersuchen müssen, welches Paket Ihren Absturz verursacht, sollten Sie TooLargeTool ausprobieren .

(Ich fand dies als Kommentar von @Max Spencer unter der akzeptierten Antwort und es war in meinem Fall hilfreich.)

Sulai
quelle
9
Am meisten unterschätzte Lösung. Dieses Tool hilft Ihnen, die beleidigenden Aktivitäten
einzugrenzen
Dieses Tool hat mir geholfen, mein Problem zu lösen. Einfach zu installieren und zu verwenden.
Carlos
Dieses Tool ist für Kotlin: / Gibt es eine Alternative für Java?
MaxwellNewage
2
@maxwellnewage: Anscheinend funktioniert die neueste Version (0.2.1, 0.2.0 auch) derzeit nicht in Nur-Java-Anwendungen. Ich musste Version 0.1.6 verwenden und es hat dann gut funktioniert
Heisenberg
Mit diesem Tool stelle ich fest, dass ich in meinen Fragmenten sehr viele Bündel verwendet habe. Was ich getan habe, ist, das Argument aus dem Bündel zu extrahieren und das Bündel mitbundle.clear()
EJ Chathuranga
41

Dies ist keine endgültige Antwort, kann jedoch Aufschluss über die Ursachen von a geben TransactionTooLargeExceptionund dabei helfen, das Problem zu lokalisieren.

Obwohl sich die meisten Antworten auf große übertragene Datenmengen beziehen, wird diese Ausnahme zufällig nach starkem Scrollen und Zoomen und wiederholtem Öffnen eines ActionBar-Drehfeldmenüs ausgelöst. Der Absturz tritt beim Tippen auf die Aktionsleiste auf. (Dies ist eine benutzerdefinierte Mapping-App)

Die einzigen Daten, die weitergegeben werden, scheinen Berührungen vom "Input Dispatcher" an die App zu sein. Ich denke, dass dies im "Transaktionspuffer" nicht annähernd 1 MB betragen kann.

Meine App läuft auf einem 1,6-GHz-Quad-Core-Gerät und verwendet 3 Threads für das Heavy-Lifting, wobei ein Core für den UI-Thread frei bleibt. Darüber hinaus verwendet die App android: largeHeap, hat noch 10 MB unbenutzten Heap und 100 MB Platz, um den Heap zu vergrößern. Ich würde also nicht sagen, dass es sich um ein Ressourcenproblem handelt.

Dem Absturz gehen immer unmittelbar folgende Zeilen voraus:

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

Die nicht unbedingt in dieser Reihenfolge gedruckt werden, sondern (soweit ich das überprüft habe) in derselben Millisekunde erfolgen.

Und die Stapelverfolgung selbst ist der Klarheit halber dieselbe wie in der Frage:

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

Wenn man sich mit dem Quellcode von Android befasst, findet man folgende Zeilen:

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

Für mich klingt es so, als würde ich möglicherweise auf diese undokumentierte Funktion stoßen, bei der die Transaktion aus anderen Gründen als einer zu großen Transaktion fehlschlägt. Sie hätten es benennen sollen TransactionTooLargeOrAnotherReasonException.

Zu diesem Zeitpunkt habe ich das Problem nicht gelöst, aber wenn ich etwas Nützliches finde, werde ich diese Antwort aktualisieren.

Update: Es stellte sich heraus, dass mein Code einige Dateideskriptoren durchgesickert ist, deren Anzahl unter Linux maximiert ist (normalerweise 1024), und dies scheint die Ausnahme ausgelöst zu haben. Es war also doch ein Ressourcenproblem. Ich habe dies durch /dev/zero1024- maliges Öffnen überprüft , was zu allerlei seltsamen Ausnahmen bei Aktionen im Zusammenhang mit der Benutzeroberfläche führte, einschließlich der obigen Ausnahme und sogar einiger SIGSEGVs. Anscheinend wird das Versagen beim Öffnen einer Datei / eines Sockets in Android nicht sehr sauber gehandhabt / gemeldet.

mvds
quelle
36

Das TransactionTooLargeException plagt uns seit ungefähr 4 Monaten und wir haben das Problem endlich gelöst!

Was geschah, war, dass wir a FragmentStatePagerAdapterin a verwendenViewPager . Der Benutzer blättert durch und erstellt mehr als 100 Fragmente (es ist eine Leseanwendung).

Obwohl wir die Fragmente ordnungsgemäß verwalten destroyItem(), gibt es in der Androids-Implementierung FragmentStatePagerAdaptereinen Fehler, bei dem auf die folgende Liste verwiesen wurde:

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

Und wenn Android FragmentStatePagerAdapterversucht, den Status zu speichern, wird die Funktion aufgerufen

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

Wie Sie sehen können, FragmentStatePagerAdapterspeichert die Basisklasse auch dann, wenn Sie die Fragmente in der Unterklasse ordnungsgemäß verwalten , ein Fragment.SavedStatefür jedes einzelne jemals erstellte Fragment. Das TransactionTooLargeExceptionwürde auftreten, wenn dieses Array auf a ausgegeben wurde parcelableArrayund das Betriebssystem es nicht mehr als 100 Elemente mögen würde.

Deshalb ist die Lösung für uns war es, die außer Kraft zu setzen saveState()Methode und nicht für den Shop etwas "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;
}
IK828
quelle
beste Antwort für mich. Vielen Dank
Pavel
Von allen Möglichkeiten dafür schien mir dies nur eine Option zu sein. Was ist in dem Zustand gespeichert, der es ihm ermöglichen würde, diese Größe zu erreichen? Wenn der Status so dringend benötigt wird, dass Code zum Speichern vorhanden ist, wie verursacht dies keine anderen Probleme? Vielen Dank
Kenny
Gleiche Frage wie @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
Ich lasse nur die letzten 3 Zustände, überprüfe den Code oben.
Ramy Sabry
20

Wenn Sie auf der Suche nach einer Antwort auf die Frage, warum die TransactionTooLargeException angezeigt wird, bitter enttäuscht sind, versuchen Sie zu überprüfen, wie viele Informationen Sie im Status der Instanz speichern.

Auf compile / targetSdkVersion <= 23 haben wir nur eine interne Warnung über die Größe des gespeicherten Status, aber nichts ist abgestürzt:

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)

Aber bei compile / targetSdkVersion> = 24 haben wir in diesem Fall einen echten RuntimeException-Absturz :

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) 

Was ist zu tun?

Speichern Sie Daten in der lokalen Datenbank und behalten Sie nur die IDs im Instanzstatus bei, mit denen Sie diese Daten abrufen können.

Yazon2006
quelle
Ist es möglich, es als globalen Parameter beizubehalten und später zu verwenden?
Jitendra Ramoliya
@ Jitendraramoliya Ja, das kannst du. Genau das meine ich.
Yazon2006
13

Diese Ausnahme wird normalerweise ausgelöst, wenn die App in den Hintergrund gesendet wird.

Deshalb habe ich mich entschlossen, die Datenfragment-Methode zu verwenden, um das vollständig zu umgehen onSavedInstanceStae Lebenszyklus . Meine Lösung verarbeitet auch komplexe Instanzzustände und gibt so schnell wie möglich Speicher frei.

Zuerst habe ich ein einfaches Fargement zum Speichern der Daten erstellt:

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

Bei meiner Hauptaktivität umgehe ich dann den gespeicherten Instanzzyklus vollständig und verschiebe die Verantwortlichkeit für mein Datenfragment. Sie müssen dies nicht für die Fragmente selbst verwenden, da ihr Status automatisch zum Status der Aktivität hinzugefügt wird.

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

Was bleibt, ist einfach die gespeicherte Instanz zu öffnen:

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

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

Ausführliche Informationen: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/

Vaiden
quelle
1
Was passiert, wenn die Aktivität zerstört wird, wenn sich die App im Hintergrund befindet und Sie versuchen, die Aktivität in den Vordergrund zu stellen?
Master Disaster
@MasterDisaster In diesem Fall wird kein Status gespeichert, da der Prozess, der das Instanzfragment enthält, gestorben ist.
Vaiden
Rechte, daher funktioniert dieser Fall nur bei Konfigurationsänderungen.
Master Disaster
Es funktioniert immer dann, wenn das Betriebssystem ausgelöst wird onSavedState(), was in vielen Fällen der Fall ist. Konfigurationsänderung ist eine. Das Wechseln von Apps und das Wechseln in den Hintergrund sind weitere. Und es gibt noch mehr.
Vaiden
1
Ich denke, diese Lösung sollte erweitert werden, um Daten aus verschiedenen Quellen zu speichern. Wahrscheinlich eine HashMap mit Tags als Schlüssel und Bündeln als Werte.
CoolMind
11

Es gibt keine spezifische Ursache für dieses Problem. In meiner Fragment-Klasse habe ich Folgendes getan:

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

an Stelle von:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);
Ojonugwa Jude Ochalifu
quelle
9

Es ist wichtig zu verstehen, dass der Transaktionspuffer unabhängig von den Gerätefunktionen oder der App auf 1 MB begrenzt ist. Dieser Puffer wird bei allen API-Aufrufen verwendet und von allen Transaktionen gemeinsam genutzt, die derzeit von einer App ausgeführt werden.

Ich glaube, es enthält auch ein bestimmtes Objekt wie Pakete und dergleichen (Parcel.obtain()), daher ist es wichtig, immer jedes obtain()mit einem abzugleichen recycle().

Dieser Fehler kann leicht bei API-Aufrufen auftreten, die viele Daten zurückgeben, obwohl die zurückgegebenen Daten weniger als 1 MB betragen (wenn noch andere Transaktionen ausgeführt werden).

Der PackageManager.getInstalledApplication()Aufruf gibt beispielsweise eine Liste aller installierten Apps zurück. Durch Hinzufügen bestimmter Flags können viele zusätzliche Daten abgerufen werden. Dies schlägt wahrscheinlich fehl. Es wird daher empfohlen, keine zusätzlichen Daten abzurufen und diese pro App abzurufen.

Der Anruf kann jedoch immer noch fehlschlagen. Daher ist es wichtig, ihn mit einem zu umgeben catchund bei Bedarf erneut versuchen zu können.

Soweit ich weiß, gibt es keine Lösung für ein solches Problem, außer es erneut zu versuchen und sicherzustellen, dass so wenig Informationen wie möglich abgerufen werden.

3c71
quelle
Vielen Dank für die Information, dass der Puffer von allen Transaktionen innerhalb der Anwendung geteilt wird!
Artem Mostyaev
1
Wenn dies der Fall wäre, warum bin ich dann nur auf Android 4.4-Handys und nirgendwo anders. Ich denke, es ist eher ein Fehler in 4.4, dass ich nicht herausfinden kann, warum.
JPM
9

Ich habe auch diese Ausnahme auf einem Samsung S3. Ich vermute 2 Grundursachen,

  1. Sie haben Bitmaps, die zu viel Speicher laden und belegen. Verwenden Sie Downsizing
  2. In den Ordnern drawable-_dpi fehlen einige Drawables. Android sucht sie in Drawable und ändert ihre Größe, sodass Ihre setContentView plötzlich springt und viel Speicher benötigt.

Verwenden Sie DDMS und sehen Sie sich Ihren Heap an, während Sie Ihre App abspielen. Dies gibt Ihnen einen Hinweis darauf, welche setcontentview das Problem verursacht.

Ich habe alle Drawables in alle Ordner kopiert, um Problem 2 zu beseitigen.

Problem ist behoben.

Siddharth
quelle
Die Speicher- / Bitmap-Ausnahmen sehen normalerweise anders aus. Ich habe bereits viele Tests mit Android 2.x - 4.x gesehen und die Ausnahmen sehen immer anders aus. Aber wer weiß, vielleicht ist dies auch verwandt, aber spezifisch für 4.x-Versionen.
Ixx
10
Es ist nur eine schreckliche Ausnahme in Bezug auf Informationen, da es keinen Hinweis darauf gibt, woher das Problem kommt.
Ixx
Ich denke, die paar Tage sind vorbei. Was sind Ihre Ergebnisse?
Denny
8

Fügen Sie dies Ihrer Aktivität hinzu

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

Es funktioniert bei mir, hoffe es hilft dir auch

Makvin
quelle
7
Ich denke, es ist der schädlichste Hinweis. Warum sollten wir Daten löschen, in denen wir wiederherstellen möchten onCreate()?
CoolMind
2
Die Implikationen dieses Codes sind, dass Sie am Ende NICHT Ihren
Justin
4

Für uns war es also der Versuch, ein zu großes Objekt über unsere AIDL-Schnittstelle an einen Remote-Dienst zu senden. Die Transaktionsgröße darf 1 MB nicht überschreiten. Die Anforderung wird in separate Blöcke von 512 KB unterteilt und einzeln über die Schnittstelle gesendet. Eine brutale Lösung, die ich kenne, aber hey - es ist Android :(

Kevin Parker
quelle
4

Sie haben Ihren alten InstanceState von der onSaveInstanceState- Methode gelöscht , und es wird gut funktionieren. Ich verwende FragmentStatePagerAdapter für meinen Viewpager, daher behalte ich die Überschreibungsmethode in meiner übergeordneten Aktivität bei, um InstanceState zu löschen.

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

Ich habe diese Lösung von hier aus gefunden android.os.TransactionTooLargeException auf Nougat

varotariya vajsi
quelle
Ich danke dir sehr.
Andrain
Vielen Dank für Ihre Antwort helfen Sie mir
Jatin Patel
3

Kürzlich bin ich auch auf einen interessanten Fall gestoßen, als ich mit dem Kontaktanbieter von Android gearbeitet habe .

Ich musste Fotos von Kontakten aus der internen Kontaktdatenbank laden und gemäß der Systemarchitektur werden alle diese Daten durch Abfragen an den Kontaktanbieter geliefert.

Da es sich um eine separate Anwendung handelt, werden alle Arten der Datenübertragung mithilfe des Binder-Mechanismus ausgeführt, sodass hier der Binder-Puffer ins Spiel kommt.

Mein Hauptfehler war , dass ich nicht in der Nähe war das Cursormit Blob - Daten aus den Kontakten Provider bekommen, so dass der Speicher für den Provider zugewiesen erhöht und diese aufgeblasen der Binder puffern , bis ich Tonnen bekam von!!!FAILED BINDER TRANSACTION!!! Nachrichten in meinem LogCat ausgegeben.

Die Hauptidee ist also, dass Sie, wenn Sie mit externen Inhaltsanbietern arbeiten und Cursors von diesen erhalten, diese immer schließen, wenn Sie mit ihnen fertig sind.

Alex Bonel
quelle
3

Ich hatte das gleiche Problem, als ich versuchte, eine Bitmap über Intent zu senden, und gleichzeitig, als es passierte, faltete ich die Anwendung.

Wie es in diesem Artikel beschrieben wird, geben Sie hier die Linkbeschreibung ein eine Linkbeschreibung ein. geschieht, wenn eine Aktivität gerade gestoppt wird. bedeutet, dass die Aktivität versucht hat, ihre gespeicherten Statuspakete an das Systembetriebssystem zu senden, um sie später (nach einer Konfigurationsänderung) sicher wiederherzustellen oder Prozess Tod), aber dass eines oder mehrere der von ihm gesendeten Bundles zu groß waren.

Ich habe es per Hack gelöst, indem ich onSaveInstanceState in meiner Aktivität überschrieben habe:

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

und Kommentar super anrufen. Es ist ein schmutziger Hack, aber es funktioniert perfekt. Bitmap wurde erfolgreich ohne Abstürze gesendet. Hoffe das wird jemandem helfen.

Anonym
quelle
2

In meinem Fall erhalte ich TransactionTooLargeException als sekundären Absturz, nachdem die native Bibliothek mit SIGSEGV abgestürzt ist. Der Absturz der nativen Bibliothek wird nicht gemeldet, daher erhalte ich nur TransactionTooLargeException.

chris
quelle
2

Ich habe dies in meinem Syncadapter erhalten, als ich versuchte, einen großen ContentValues ​​[] einzufügen. Ich habe beschlossen, es wie folgt zu beheben:

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));
}
Pepijn
quelle
2
Was ist, wenn der andere versagt? Sie müssen mithilfe einer Schleife mehr teilen. Gibt es vielleicht eine Möglichkeit, die Transaktionsgröße und die maximale Transaktionsgröße zu ermitteln?
Android-Entwickler
2

Für mich war es auch das FragmentStatePagerAdapter, aber das Überschreiben saveState()hat nicht funktioniert. So habe ich es behoben:

Behalten Sie beim Aufrufen des FragmentStatePagerAdapterKonstruktors eine separate Liste der Fragmente in der Klasse bei und fügen Sie eine Methode zum Entfernen der Fragmente hinzu:

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

Dann in der Activity, speichern Sie die ViewPagerPosition und Ruf adapter.removeFragments()in der überschriebenen onSaveInstanceState()Methode:

private int pagerPosition;

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

Zuletzt onResume()instanziieren Sie in der überschriebenen Methode den Adapter erneut, wenn dies nicht der Fall ist null. (Wenn es nulldann das Activitywird zum ersten Mal geöffnet oder nach der App von Android abgetötet wurde, bei dem onCreatedie Adaptererstellung tun.)

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

Stellen Sie sicher, dass Sie keine großen Objektdaten in Intent einfügen. In meinem Fall habe ich String 500k Größe hinzugefügt und dann eine andere Aktivität gestartet. Mit dieser Ausnahme ist es immer fehlgeschlagen. Ich habe es vermieden, Daten zwischen Aktivitäten auszutauschen, indem ich statische Variablen von Aktivitäten verwendet habe - Sie müssen sie nicht an Intent senden und dann daraus ziehen.

Was ich hatte:

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);
gladimdim
quelle
Das ist eine sehr schlechte Idee. Die Verwendung statischer Variablen auf diese Weise ist ein Codegeruch. Versuchen Sie außerdem, diesen Code mit dem Flag "Aktivitäten nicht beibehalten" auszuführen, und drücken Sie nach dem Navigieren zu PageWebView die Starttaste. Nachdem Sie Ihre Aktivität erneut geöffnet haben, wird es wahrscheinlich zu einem Absturz kommen, da MainActivity mit all seinen Variablen zu 100% tot ist.
Kyrylo Zapylaiev
1

Wenn ich mich mit dem WebViewin meiner App beschäftige, passiert es. Ich denke, es hängt mit addViewund UI-Ressourcen zusammen. In meiner App füge ich WebViewActivityunten einen Code hinzu, dann läuft er in Ordnung:

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

Ich habe die Hauptursache dafür gefunden (wir haben sowohl "Fenster hinzugefügt" fehlgeschlagen als auch Dateideskriptor-Leck, wie mvds sagt).

Es gibt einen Fehler in BitmapFactory.decodeFileDescriptor()Android 4.4. Es tritt nur auf, wenn inPurgeableund inInputShareablevon eingestellt BitmapOptionssindtrue . Dies führt an vielen Stellen zu Problemen bei der Interaktion mit Dateien.

Beachten Sie, dass die Methode auch von aufgerufen wird MediaStore.Images.Thumbnails.getThumbnail() .

Universal Image Loader ist von diesem Problem betroffen. Picasso und Glide scheinen nicht betroffen zu sein. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020

ypresto
quelle
1

Diese eine Codezeile in der Methode writeToParcel (Parcel dest, int flags) hat mir geholfen, die TransactionTooLargeException loszuwerden.

dest=Parcel.obtain(); 

Nur nach diesem Code schreibe ich alle Daten in das Paketobjekt, dh dest.writeInt () usw.

dilip
quelle
1

Versuchen Sie, eine Lösung zu verwenden EventBusoder zu ContentProvidermögen.

Wenn Sie sich im selben Prozess befinden (normalerweise sind dies alle Ihre Aktivitäten), versuchen Sie es zu verwenden EventBus, da der Datenaustausch im Prozess KEINEN Puffer benötigt, sodass Sie sich keine Sorgen machen müssen, dass Ihre Daten zu groß sind. (Sie können einfach einen Methodenaufruf verwenden, um Daten zu übergeben, und EventBus versteckt die hässlichen Dinge.) Hier ist das Detail:

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

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

Wenn sich die beiden Seiten von Intent nicht im selben Prozess befinden, versuchen Sie etwas ContentProvider.


Siehe TransactionTooLargeException

Die Binder-Transaktion ist fehlgeschlagen, weil sie zu groß war.

Während eines Remoteprozeduraufrufs werden die Argumente und der Rückgabewert des Aufrufs als Paketobjekte übertragen, die im Binder-Transaktionspuffer gespeichert sind. Wenn die Argumente oder der Rückgabewert zu groß sind, um in den Transaktionspuffer zu passen, schlägt der Aufruf fehl und TransactionTooLargeException wird ausgelöst.

Boileryao
quelle
1

Ich habe eine TransactionTooLargeException aufgrund eines Stackoverflow-Fehlers in einem Android-Espresso-Test erhalten. Ich habe den Stackoverflow-Fehler-Stack-Trace in den Protokollen gefunden, als ich den Logcat-Filter für meine App entfernt habe.

Ich vermute, dass Espresso die TransactionTooLargeException verursacht hat, als versucht wurde, eine wirklich große Ausnahme-Stacktrace zu verarbeiten.

Dagmar
quelle
1

Man kann verwenden:

android:largeHeap="true"

im Android Manifest unter Application Tag.

Dies löste das Problem in meinem Fall!

MazRoid
quelle
In meinem Fall (weil ich onSaveInstantStateAktivitäten / Fragmente aufgerufen und große Listen gespeichert habe) hat es nicht geholfen.
CoolMind
1
Dies wird als schlechte Vorgehensweise angesehen, da Sie sich nicht mit dem Grund befassen, warum überhaupt viele Daten gespeichert werden. Auf neueren Geräten stürzt die App ab und das Limit ist viel kleiner (256 KB). Untersuchen Sie zuerst, warum Sie Lose speichern, und reduzieren Sie diese.
Tim Kist
1

Ich hatte auch dieses Problem mit Bitmap-Daten, die von einer Aktivität zur nächsten übertragen wurden, aber ich mache eine Lösung, indem ich meine Daten als statische Daten mache, und dies funktioniert perfekt für mich

In der Aktivität zuerst:

public static Bitmap bitmap_image;

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

und in der zweiten Aktivität:

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

Dies geschah in meiner App, weil ich eine Liste von Suchergebnissen in den Argumenten eines Fragments übergab und diese Liste einer Eigenschaft des Fragments zuwies - die eigentlich ein Verweis auf denselben Speicherort ist, auf den die Argumente des Fragments zeigen - und dann hinzufügte neue Elemente in der Liste, die auch die Größe der Argumente des Fragments geändert haben. Wenn die Aktivität angehalten wird, versucht die Basisfragmentklasse, die Argumente des Fragments in onSaveInstanceState zu speichern. Dies stürzt ab, wenn die Argumente größer als 1 MB sind. Beispielsweise:

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

Die einfachste Lösung in diesem Fall bestand darin, der Eigenschaft des Fragments eine Kopie der Liste zuzuweisen, anstatt eine Referenz zuzuweisen:

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

Eine noch bessere Lösung wäre, nicht so viele Daten in den Argumenten weiterzugeben.

Ohne diese Antwort und TooLargeTool hätte ich das wahrscheinlich nie gefunden .

Big McLargeHuge
quelle
1

Ich habe auch TransactionTooLargeException gelebt. Erstens habe ich daran gearbeitet zu verstehen, wo es auftritt. Ich kenne den Grund, warum es auftritt. Jeder von uns weiß es wegen des großen Inhalts. Mein Problem war so und ich löste. Vielleicht kann diese Lösung für jeden nützlich sein. Ich habe eine App, die Inhalte von API erhält. Ich erhalte das Ergebnis der API im ersten Bildschirm und sende es an den zweiten Bildschirm. Ich kann diesen Inhalt erfolgreich an den zweiten Bildschirm senden. Nach dem zweiten Bildschirm tritt diese Ausnahme auf, wenn ich zum dritten Bildschirm wechseln möchte. Jeder meiner Bildschirme wird aus Fragment erstellt. Ich bemerkte das, als ich vom zweiten Bildschirm ging. Es speichert seinen Bundle-Inhalt. Wenn dieser Inhalt zu groß ist, tritt diese Ausnahme auf. Meine Lösung ist, nachdem ich Inhalte aus dem Bundle erhalten habe, lösche ich sie.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }
Nebyan
quelle
Während dies richtig ist (schade für mich, ich habe dasselbe nach einem Jahr verstanden), wie wird sich das Fragment verhalten, wenn es neu erstellt wird (Bildschirmrotation)?
CoolMind
0

Eine Lösung wäre, dass die App die ArrayList (oder das Objekt, das das Problem verursacht) in das Dateisystem schreibt, dann einen Verweis auf diese Datei (z. B. Dateiname / Pfad) über den Intent an den IntentService übergibt und dann den IntentService überlässt Rufen Sie den Dateiinhalt ab und konvertieren Sie ihn zurück in eine ArrayList.

Wenn der IntentService mit der Datei fertig ist, sollte er sie entweder löschen oder die Anweisung über einen lokalen Broadcast an die App zurückgeben, um die von ihm erstellte Datei zu löschen (wobei dieselbe Dateireferenz zurückgegeben wird, die ihm bereitgestellt wurde).

Weitere Informationen finden Sie in meiner Antwort auf dieses Problem .

Ban-Geoengineering
quelle
0

Als Absichten, Inhaltsanbieter, Messenger verwenden alle Systemdienste wie Telefon, Vibrator usw. den IPC-Infrastrukturanbieter von Binder. Darüber hinaus verwenden die Rückrufe im Aktivitätslebenszyklus auch diese Infrastruktur.

1 MB ist das Gesamtlimit für alle Bindertransaktionen, die zu einem bestimmten Zeitpunkt im System ausgeführt werden.

Wenn beim Senden der Absicht viele Transaktionen stattfinden, kann dies fehlschlagen, obwohl zusätzliche Daten nicht groß sind. http://codetheory.in/an-overview-of-android-binder-framework/

Shinoo Goyal
quelle
0

Bei so vielen Stellen, an denen TransactionTooLargeException auftreten kann - hier ist eine weitere Neuerung für Android 8 - ein Absturz, wenn jemand nur anfängt, einen EditText einzugeben, wenn der Inhalt zu groß ist.

Es bezieht sich auf den AutoFillManager (neu in API 26) und den folgenden Code in StartSessionLocked():

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

Wenn ich das richtig verstehe, ruft dies den AutoFill-Dienst auf und übergibt den AutofillManagerClient im Ordner. Und wenn der EditText viel Inhalt hat, scheint er den TTLE zu verursachen.

Ein paar Dinge können es mildern (oder haben es sowieso getan, wie ich es getestet habe): Fügen Sie android:importantForAutofill="noExcludeDescendants"die XML-Layoutdeklaration von EditText hinzu. Oder im Code:

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

Eine zweite schreckliche, schreckliche Problemumgehung könnte auch darin bestehen, die Methoden performClick()und zu überschreiben onWindowFocusChanged(), um den Fehler in einer TextEdit-Unterklasse selbst abzufangen. Aber ich denke nicht, dass das wirklich klug ist ...

Fett
quelle