Co robić w TransactionTooLargeException


239

Mam TransactionTooLargeException. Niereprodukowalny. W dokumentacji jest napisane

Transakcja Binder nie powiodła się, ponieważ była zbyt duża.

Podczas zdalnego wywołania procedury argumenty i wartość zwracana wywołania są przesyłane jako obiekty Parcel przechowywane w buforze transakcji Binder. Jeśli argumenty lub wartość zwracana są zbyt duże, aby zmieścić się w buforze transakcji, wywołanie zakończy się niepowodzeniem i zostanie zgłoszony wyjątek TransactionTooLargeException.

...

Istnieją dwa możliwe wyniki, gdy zdalne wywołanie procedury zgłosi wyjątek TransactionTooLargeException. Albo klient nie był w stanie wysłać swojego żądania do usługi (najprawdopodobniej, jeśli argumenty były zbyt duże, aby zmieściły się w buforze transakcji), lub usługa nie była w stanie odesłać swojej odpowiedzi z powrotem do klienta (najprawdopodobniej, jeśli wartość zwracana to zbyt duży, aby zmieścił się w buforze transakcji).

...

Więc gdzieś przekazuję lub odbieram argumenty przekraczające nieznany limit. Gdzie?

Stacktrace nie pokazuje nic użytecznego:

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)

Wydaje się, że jest to związane z widokami? Jak to się ma do zdalnego wywoływania procedur?

Może ważne: wersja Android: 4.0.3, urządzenie: HTC One X


Nie. Ale nie dostałem tego ponownie. Posiadaj moduł do śledzenia błędów w aplikacji na żywo i otrzymaj go tylko raz w ciągu około 3 tygodni. Przynajmniej nie zdarza się to często. Może warto otworzyć problem na Androidzie, chociaż ...
Ixx

Nie mam odpowiedzi, ale to niezawodnie powoduje twardy reset mojego Galaxy S2.
Timmmm,

Właśnie to miało miejsce dzisiaj w jednej z moich aplikacji. Stało się to tylko raz i z Galaxy S3. Interesujące jest to, że wydaje się to być ujmujące tylko w przypadku mocniejszych urządzeń.
danwms

ten wyjątek został dodany w API 15, developer.android.com/reference/android/os/... I odtworzyłem go w MapView podczas przewijania mapy. dopóki GC nie napisał, że nie mam już pamięci. (Zajęło mi to kilka minut)
Meh

Bufor transakcji jest ograniczony do 1 MB na wszystkich urządzeniach, a ten bufor przedawnia każdą transakcję. Im mocniejsze urządzenie, tym więcej transakcji może wykonywać jednocześnie, z których wszystkie zużywają ten sam bufor 1 MB. To powiedziawszy, twoja odpowiedź nie jest odpowiedzią, ale komentarzem.
3c71

Odpowiedzi:


158

Zetknąłem się z tym problemem i odkryłem, że kiedy ogromna ilość danych jest wymieniana między usługą a aplikacją (wiąże się to z przenoszeniem wielu miniatur). W rzeczywistości rozmiar danych wynosił około 500 kb, a rozmiar bufora transakcji IPC jest ustawiony na 1024 kB. Nie jestem pewien, dlaczego przekroczył bufor transakcji.

Może się to również zdarzyć, gdy przekazujesz dużo danych przez zamierzone dodatki

Po otrzymaniu tego wyjątku w aplikacji przeanalizuj kod.

  1. Czy wymieniasz dużo danych między swoimi usługami a aplikacją?
  2. Korzystając z zamiarów do udostępniania ogromnych danych (na przykład użytkownik wybiera ogromną liczbę plików z udziału prasowego w galerii, identyfikatory URI wybranych plików zostaną przesłane za pomocą zamiarów)
  3. odbieranie plików bitmap z usługi
  4. oczekiwanie na odpowiedź Androida z ogromnymi danymi (na przykład getInstalledApplications (), gdy użytkownik zainstalował wiele aplikacji)
  5. za pomocą metody applyBatch () z dużą liczbą oczekujących operacji

Jak postępować, gdy pojawi się ten wyjątek

Jeśli to możliwe, podziel dużą operację na małe porcje, na przykład zamiast wywoływać komendę applyBatch () przy użyciu 1000 operacji, wywołuj ją po 100.

Nie wymieniaj dużych danych (> 1 MB) między usługami i aplikacjami

Nie wiem, jak to zrobić, ale nie pytaj Androida, który może zwrócić ogromne dane :-)


1
Załóżmy, że sprawdzam, czy plik .apk jest zainstalowany, czy też nie? w czasie instalacji ... Otrzymuję ten sam wyjątek podczas sprawdzania mojego pakietu com.test.installedornot.My .apk rozmiar jest większy niż 9 MB, w takim razie jak poradzę sobie z tym wyjątkiem?
DJhon

15
Otrzymuję ten wyjątek dokładnie podczas połączenia z getInstalledApplications. Co można zrobić, aby to rozwiązać?
Stan

1
@Stan Ten interfejs API jest powszechny i ​​używany szeroko w aplikacjach na Androida. Ten wyjątek naprawdę mnie martwi, kiedy korzystam z tego interfejsu API.
peacepassion

7
Mogę potwierdzić twoje wnioski dotyczące limitu wynoszącego około 500 KB, ale jest on specyficzny dla urządzenia, na niektórych urządzeniach możesz przenieść prawie cały 1 MB. Miałem również ten wyjątek, więc przeprowadziłem dochodzenie i napisałem post, który może być interesujący dla osób mających ten problem. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

11
Jeśli trudno jest dokładnie ustalić, który stan powoduje awarię, przydatne może być TooLargeTool .
Max Spencer

48

Jeśli chcesz sprawdzić, która paczka powoduje awarię, powinieneś rozważyć skorzystanie z TooLargeTool .

(Znalazłem to jako komentarz od @Max Spencer pod przyjętą odpowiedzią i było to pomocne w moim przypadku).


9
Najbardziej niedoceniane rozwiązanie. To narzędzie pomaga zawęzić przestępcze działania
Kedar Paranjape

To narzędzie, które pomogło rozwiązać mój problem. Łatwy w instalacji i obsłudze.
Carlos

to narzędzie jest dla kotlin: / jakaś alternatywa dla java?
maxwellnewage

2
@maxwellnewage: wygląda na to, że najnowsza wersja (0.2.1, 0.2.0 też) obecnie nie działa w aplikacjach tylko Java. Musiałem użyć wersji 0.1.6 i wtedy to działało dobrze
heisenberg

Za pomocą tego narzędzia wykrywam, że w swoich fragmentach korzystałem z ogromnych rozmiarów pakietów. To, co zrobiłem, to wyciągnięcie argumentu z pakietu i wyczyszczenie pakietu za pomocąbundle.clear()
EJ Chathuranga

41

To nie jest ostateczna odpowiedź, ale może rzucić nieco światła na przyczyny a TransactionTooLargeExceptioni pomóc w zlokalizowaniu problemu.

Chociaż większość odpowiedzi odnosi się do dużej ilości przesyłanych danych, widzę ten wyjątek zgłoszony przypadkowo po intensywnym przewijaniu i powiększaniu oraz wielokrotnym otwieraniu menu pokrętła ActionBar. Awaria następuje po dotknięciu paska akcji. (jest to niestandardowa aplikacja do mapowania)

Wydaje się, że jedynymi przekazywanymi danymi są poprawki z „Input Dispatchera” do aplikacji. Myślę, że nie może to rozsądnie wynosić prawie 1 mb w „Buforze transakcji”.

Moja aplikacja działa na czterordzeniowym urządzeniu 1,6 GHz i wykorzystuje 3 wątki do podnoszenia ciężarów, dzięki czemu jeden rdzeń jest wolny dla wątku interfejsu użytkownika. Ponadto aplikacja korzysta z Androida: largeHeap, pozostało 10 mb nieużywanej sterty i pozostało 100 mb miejsca na wyhodowanie sterty. Więc nie powiedziałbym, że to problem z zasobami.

Awaria zawsze poprzedza następujące linie:

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

Które niekoniecznie są drukowane w tej kolejności, ale (o ile sprawdziłem) zdarzają się w tej samej milisekundie.

A sam ślad stosu, dla jasności, jest taki sam jak w pytaniu:

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

Wchodząc w kod źródłowy Androida, można znaleźć następujące linie:

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

Dla mnie brzmi to tak, jakbym prawdopodobnie korzystał z tej nieudokumentowanej funkcji, w której transakcja kończy się niepowodzeniem z innych powodów niż Transakcja będąca zbyt duża. Powinni byli to nazwać TransactionTooLargeOrAnotherReasonException.

W tej chwili nie rozwiązałem problemu, ale jeśli znajdę coś przydatnego, zaktualizuję tę odpowiedź.

aktualizacja: okazało się, że mój kod wyciekł z niektórych deskryptorów plików, których liczba jest zmaksymalizowana w systemie Linux (zwykle 1024), i wydaje się, że spowodowało to wyjątek. W końcu był to problem z zasobami. Sprawdziłem to, otwierając /dev/zero1024 razy, co spowodowało różnego rodzaju dziwne wyjątki w działaniach związanych z interfejsem użytkownika, w tym wyjątek powyżej, a nawet niektóre SIGSEGV. Najwyraźniej brak otwarcia pliku / gniazda nie jest czymś, co jest obsługiwane / zgłaszane bardzo czysto na Androidzie.


36

TransactionTooLargeExceptionZostał dręczące nas przez około 4 miesięcy, a my w końcu rozwiązany!

To, co się działo, było to, że używamy FragmentStatePagerAdapterw ViewPager. Użytkownik przeglądał strony i tworzył ponad 100 fragmentów (jest to aplikacja do czytania).

Chociaż odpowiednio zarządzamy fragmentami destroyItem(), w implementacji Androidów FragmentStatePagerAdapterwystępuje błąd, w którym zachowało się odniesienie do następującej listy:

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

A kiedy Android FragmentStatePagerAdapterpróbuje zapisać stan, wywoła tę funkcję

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

Jak widać, nawet jeśli odpowiednio zarządzasz fragmentami w FragmentStatePagerAdapterpodklasie, klasa podstawowa będzie nadal przechowywać Fragment.SavedStatedla każdego utworzonego fragmentu. TransactionTooLargeExceptionNastąpiłoby po cenach dumpingowych, że tablica została Do parcelableArraya system operacyjny nie chciałby nim 100+ szt.

Dlatego poprawką dla nas było zastąpienie saveState()metody i nie przechowywanie niczego "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;
}

najlepsza odpowiedź dla mnie. Dziękuję bardzo
pavel

Ze wszystkich możliwości tego wydawało mi się to jedyną opcją. Co jest przechowywane w stanie, który pozwoliłby mu osiągnąć ten rozmiar? Jeśli stan jest potrzebny tak bardzo, że istnieje kod do jego zapisania, w jaki sposób nie powoduje to innych problemów? Dzięki
Kenny

To samo pytanie co @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

Pozwoliłem tylko 3 ostatnie stany, sprawdź kod powyżej.
Ramy Sabry

20

Dla tych, którzy gorzko rozczarowali szukaniem odpowiedzi na pytanie, dlaczego pojawia się TransactionTooLargeException, spróbuj sprawdzić, ile informacji zapisujesz w stanie instancji.

Na compile / targetSdkVersion <= 23 mamy tylko wewnętrzne ostrzeżenie o dużym rozmiarze zapisanego stanu, ale nic nie ulega awarii:

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)

Ale na compile / targetSdkVersion> = 24 mamy prawdziwą awarię RuntimeException w tym przypadku:

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) 

Co robić?

Zapisz dane w lokalnej bazie danych i zachowaj tylko identyfikatory w stanie instancji, których możesz użyć do odzyskania tych danych.


czy można zachować go jako parametr globalny i użyć go później?
Jitendra ramoliya

@Jitendraramoliya Tak, możesz. Dokładnie o to mi chodzi.
Yazon2006,

13

Ten wyjątek jest zwykle zgłaszany, gdy aplikacja jest wysyłana w tle.

Zdecydowałem więc użyć metody Fragment danych, aby całkowicie ominąć onSavedInstanceStaecykl życia. Moje rozwiązanie obsługuje również złożone stany instancji i zwalnia pamięć jak najszybciej.

Najpierw stworzyłem prosty Fargment do przechowywania danych:

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

Następnie w ramach mojej głównej aktywności całkowicie obchodzę zapisany cykl instancji i odraczam odpowiedzialność za fragment danych. Nie trzeba tego używać na samych fragmentach, ponieważ ich stan jest automatycznie dodawany do stanu działania):

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

Pozostało po prostu wyskoczyć z zapisanej instancji:

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

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

Pełne szczegóły: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
Co się stanie, jeśli aktywność zostanie zniszczona, gdy aplikacja będzie działać w tle, a Ty spróbujesz aktywować nową wiedzę?
Master Disaster

@MasterDisaster w takim przypadku żaden stan nie jest zapisywany, ponieważ proces, który przechowuje fragment instancji, zmarł.
Vaiden

Prawa, więc ten przypadek działa tylko ze zmianą konfiguracji.
Master Disaster

Działa przy każdym uruchomieniu systemu operacyjnego onSavedState(), co zdarza się w wielu przypadkach. Zmiana konfiguracji jest jedna. Kolejne są przełączanie aplikacji i przechodzenie w tło. I jest ich więcej.
Vaiden

1
Myślę, że to rozwiązanie należy rozszerzyć, aby zapisywać dane z różnych źródeł. Prawdopodobnie HashMap z tagami jako kluczami i
paczkami

11

Nie ma jednej konkretnej przyczyny tego problemu. Dla mnie w mojej klasie Fragmentów robiłem to:

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

zamiast tego:

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

9

Ważne jest, aby zrozumieć, że bufor transakcji jest ograniczony do 1 MB, niezależnie od możliwości urządzenia lub aplikacji. Bufor ten jest używany przy każdym wykonywanym wywołaniu interfejsu API i jest współużytkowany przez wszystkie transakcje aktualnie uruchomione przez aplikację.

Wierzę, że zawiera także pewne konkretne obiekty, takie jak paczki i tym podobne (Parcel.obtain()), dlatego ważne jest, aby zawsze dopasować każdy obtain()z nich recycle().

Ten błąd może łatwo wystąpić w przypadku wywołań API zwracających dużo danych, nawet jeśli zwracane dane są mniejsze niż 1 MB (jeśli inne transakcje są nadal uruchomione).

Na przykład PackageManager.getInstalledApplication()połączenie zwraca listę wszystkich zainstalowanych aplikacji. Dodanie określonych flag pozwala odzyskać wiele dodatkowych danych. Może to się nie powieść, dlatego zaleca się, aby nie pobierać żadnych dodatkowych danych i nie pobierać ich dla poszczególnych aplikacji.

Jednak połączenie może nadal nie powieść się, dlatego ważne jest, aby otoczyć je catchi w razie potrzeby móc spróbować ponownie.

O ile mi wiadomo, nie można obejść tego problemu, oprócz ponownej próby i upewnienia się, że uzyskano jak najmniej informacji.


Dziękujemy za informację, że bufor jest współdzielony między wszystkimi transakcjami w aplikacji!
Artem Mostyaev

1
Jeśli tak było, to dlaczego dostaję tylko telefony z Androidem 4.4 i nigdzie indziej. Myślę, że to bardziej błąd w 4.4, którego nie jestem w stanie zrozumieć, dlaczego.
JPM,

9

Ja też dostałem ten wyjątek na Samsung S3. Podejrzewam 2 główne przyczyny,

  1. masz bitmapy, które ładują się i zajmują zbyt dużo pamięci, użyj zmniejszania
  2. Brakuje niektórych rysunków w folderach-dpi-rysowalnych, android szuka ich w rysunkach i zmienia ich rozmiar, powodując, że Twój setContentView nagle przeskakuje i zużywa dużo pamięci.

Użyj DDMS i spójrz na stertę podczas gry, co da ci pewne wskazówki, na którym setcontentview jest przyczyną problemu.

Skopiowałem wszystkie pliki rysunkowe we wszystkich folderach, aby pozbyć się problemu 2.

Problem został rozwiązany.


Wyjątki dotyczące pamięci / bitmapy zwykle wyglądają inaczej. Widziałem już wiele z nich testujących na Androidzie 2.x - 4.x, a wyjątki zawsze wyglądają inaczej. Ale kto wie, może to również jest powiązane, ale specyficzne dla wersji 4.x.
Ixx,

10
To tylko straszny wyjątek, jeśli chodzi o informacje, ponieważ nie daje to żadnych wskazówek co do źródła problemu.
Ixx,

Myślę, że kilka dni minęło, jakie są twoje ustalenia?
Denny,

8

Dodaj to do swojej aktywności

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

Działa to dla mnie, mam nadzieję, że również ci pomoże


7
Myślę, że to najbardziej szkodliwa wskazówka. Dlaczego powinniśmy usuwać dane, które chcemy przywrócić onCreate()?
CoolMind

2
Implikacje tego kodu są takie, że NIE zapisujesz stanu instancji ...
Justin,

4

Tak więc dla nas próbowaliśmy wysłać zbyt duży obiekt przez nasz interfejs AIDL do zdalnej usługi. Rozmiar transakcji nie może przekraczać 1 MB. Żądanie jest podzielone na osobne fragmenty o wielkości 512 KB i wysyłane pojedynczo przez interfejs. Brutalne rozwiązanie, które znam, ale hej - jego Android :(


4

Wyczyściłeś swoją starą InstanceState z metody onSaveInstanceState i będzie działać dobrze. Używam FragmentStatePagerAdapter dla mojego viewpager, więc trzymam poniżej metody Override w mojej działalności nadrzędnej dla jasnego InstanceState.

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

Znalazłem to rozwiązanie stąd android.os.TransactionTooLargeException na Nougat


Dziękuję bardzo.
Andrain

Dziękuję za odpowiedź, pomóżcie mi
Jatin Patel

3

Ostatnio spotkałem się również z ciekawym przypadkiem podczas pracy z dostawcą Kontaktów Androida .

Musiałem załadować zdjęcia kontaktów z wewnętrznej bazy danych kontaktów i zgodnie z architekturą systemu wszystkie te dane są dostarczane przez zapytania do dostawcy kontaktów.

Ponieważ działa jako osobna aplikacja - wszystkie rodzaje przesyłania danych są wykonywane przy użyciu mechanizmu Binder, dlatego też bufor Binder wchodzi w grę.

Moim głównym błędem było to, że ja nie bliskoCursor z danych BLOB otrzymali dostęp z kontaktów Provider, tak, że pamięć przydzielona dla dostawcy wzrosła i to nadmuchany spoiwa bufor aż dostałam mnóstwo !!!FAILED BINDER TRANSACTION!!!wiadomości w moim wyjściu logcat.

Więc główną ideą jest to, że kiedy pracujesz z zewnętrznymi dostawcami treści i dostajesz Cursorod nich s, zawsze zamykaj go, kiedy skończysz z nimi pracować.


3

Ten sam problem napotkałem, gdy próbowałem wysłać bitmapę przez Intent, a jednocześnie, kiedy to się stało, złożyłem aplikację.

Jak to opisano w tym artykule, wprowadź tutaj opis linku , dzieje się tak, gdy działanie jest w trakcie zatrzymywania, co oznacza, że ​​działanie próbowało wysłać swoje zapisane pakiety do systemu operacyjnego w celu bezpiecznego przechowywania w celu przywrócenia w późniejszym terminie (po zmianie konfiguracji lub proces śmierci), ale ten co najmniej jeden wysłany pakiet był zbyt duży.

Rozwiązałem go poprzez hack, zastępując onSaveInstanceState w mojej działalności:

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

i komentuj połączenie super. To brudny hack, ale działa idealnie. Bitmapa została pomyślnie wysłana bez awarii. Mam nadzieję, że to komuś pomoże.


2

W moim przypadku otrzymuję TransactionTooLargeException jako wtórną awarię po awarii biblioteki natywnej z SIGSEGV. Awaria natywnej biblioteki nie jest zgłaszana, więc otrzymuję tylko TransactionTooLargeException.


2

Mam to w moim syncadapter, gdy próbuję luzem Wstawić duże ContentValues ​​[]. Postanowiłem to naprawić w następujący sposób:

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
Co jeśli drugi zawiedzie? Musisz zrobić więcej dzielenia, używając pętli. Czy istnieje sposób na uzyskanie rozmiaru transakcji i uzyskanie maksymalnego rozmiaru transakcji?
programista Androida

2

Dla mnie było to również FragmentStatePagerAdapter, jednak ominięcie saveState()nie zadziałało. Oto jak to naprawiłem:

Podczas wywoływania FragmentStatePagerAdapterkonstruktora przechowuj osobną listę fragmentów w klasie i dodaj metodę usuwania fragmentów:

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

Następnie w Activity, zapisz ViewPagerpozycję i wywołaj metodę adapter.removeFragments()przesłoniętą onSaveInstanceState():

private int pagerPosition;

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

Wreszcie w onResume()metodzie przesłoniętej ponownie utwórz instancję adaptera, jeśli tak nie jest null. (Jeśli tak null, to Activityjest otwierany po raz pierwszy lub po tym, jak aplikacja została zabita przez system Android, w którym onCreatenastąpi utworzenie adaptera).

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

1

Upewnij się, że nie umieszczasz w danych obiektu obiektu o dużych rozmiarach. W moim przypadku dodałem rozmiar String 500k, a następnie rozpocząłem inną aktywność. Z tym wyjątkiem zawsze się nie udawało. Unikałem udostępniania danych między działaniami, używając zmiennych statycznych działań - nie musisz wysyłać ich do Intent, a następnie wyciągać z nich dane.

Co miałem:

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

To bardzo zły pomysł. Używanie zmiennych statycznych w ten sposób jest zapachem kodu. Spróbuj także uruchomić ten kod z flagą „Nie zachowuj aktywności” i naciśnij przycisk home po przejściu do PageWebView. Po ponownym otwarciu aktywności prawdopodobnie nastąpi awaria, ponieważ MainActivity będzie martwy w 100% ze wszystkimi jego zmiennymi.
Kyrylo Zapylaiev

1

Kiedy mam do czynienia z WebViewaplikacją, tak się dzieje. Myślę, że jest to związane z addViewzasobami interfejsu użytkownika. W mojej aplikacji dodaję trochę kodu w WebViewActivityten sposób poniżej, a następnie działa poprawnie:

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

1

Znalazłem główną przyczynę tego (mamy „dodawanie okna nie powiodło się” i wyciek deskryptora pliku, jak mvds mówi).

Jest błąd w BitmapFactory.decodeFileDescriptor()Androida 4.4. Występuje on tylko kiedy inPurgeablei inInputShareablez BitmapOptionssą ustawione true. Powoduje to wiele problemów w wielu miejscach interakcji z plikami.

Zauważ, że metoda jest również wywoływana z MediaStore.Images.Thumbnails.getThumbnail().

Ten problem dotyczy Universal Image Loader . Wydaje się, że nie dotyczy to Picassa i Glide . https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

Ten jeden wiersz kodu w metodzie writeToParcel (Parcel dest, int flagi) pomógł mi pozbyć się TransactionTooLargeException.

dest=Parcel.obtain(); 

Tylko po tym kodzie piszę wszystkie dane do obiektu działki, tj. Dest.writeInt () itp.


1

Spróbuj użyć EventBuslub ContentProviderpolubić rozwiązanie.

Jeśli jesteś w tym samym procesie (normalnie byłyby to wszystkie Twoje działania), spróbuj użyć EventBus, ponieważ w procesie wymiany danych NIE potrzebujesz nieco bufora, więc nie musisz się martwić, że twoje dane są zbyt duże. (Możesz po prostu użyć wywołania metody do przekazania danych, a EventBus ukrywa brzydkie rzeczy) Oto szczegół:

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

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

Jeśli dwie strony Intencji nie są w tym samym procesie, spróbuj nieco ContentProvider.


Zobacz TransactionTooLargeException

Transakcja Binder nie powiodła się, ponieważ była zbyt duża.

Podczas zdalnego wywołania procedury argumenty i wartość zwracana wywołania są przesyłane jako obiekty Parcel przechowywane w buforze transakcji Binder. Jeśli argumenty lub wartość zwracana są zbyt duże, aby zmieścić się w buforze transakcji, wywołanie zakończy się niepowodzeniem i zostanie zgłoszony wyjątek TransactionTooLargeException.


1

Wystąpił wyjątek TransactionTooLargeException z błędu Stackoverflow w teście Android Espresso. Znalazłem ślad stosu błędów przepełnienia stosu w dziennikach, gdy wyjąłem filtr Logcat dla mojej aplikacji.

Zgaduję, że Espresso spowodowało TransactionTooLargeException podczas próby obsługi naprawdę dużego śledzenia stosu wyjątków.


1

Można użyć:

android:largeHeap="true"

w Manifeście Androida pod znacznikiem aplikacji.

To rozwiązało problem w moim przypadku!


W moim przypadku (z powodu wywoływania onSaveInstantStatedziałań / fragmentów i zapisywania dużych list) nie pomogło.
CoolMind

1
Jest to uważane za złą praktykę, ponieważ nie masz do czynienia z powodem, dla którego wiele danych jest zapisywanych w pierwszej kolejności. Na nowszych urządzeniach powoduje awarię aplikacji, a limit jest znacznie mniejszy (256 KB). Dowiedz się, dlaczego najpierw przechowujesz dużo i zmniejsz je.
Tim Kist

1

Miałem również do czynienia z tym problemem związanym z przekazywaniem danych bitmapowych z jednej aktywności do drugiej, ale rozwiązuję to, tworząc moje dane jako dane statyczne, co działa idealnie dla mnie

W pierwszej kolejności:

public static Bitmap bitmap_image;

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

i w drugiej aktywności:

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

1

Działo się tak w mojej aplikacji, ponieważ przekazywałem listę wyników wyszukiwania w argumentach fragmentu, przypisując tę ​​listę do właściwości fragmentu - która w rzeczywistości jest odniesieniem do tej samej lokalizacji w pamięci wskazywanej przez argumenty fragmentu - a następnie dodałem nowe pozycje na liście, które również zmieniły rozmiar argumentów fragmentu. Gdy działanie jest zawieszone, podstawowa klasa fragmentu próbuje zapisać argumenty fragmentu w onSaveInstanceState, który ulega awarii, jeśli argumenty są większe niż 1 MB. Na przykład:

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

Najłatwiejszym rozwiązaniem w tym przypadku było przypisanie kopii listy do właściwości fragmentu zamiast przypisania odwołania:

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

Jeszcze lepszym rozwiązaniem byłoby nie przekazywanie tak wielu danych w argumentach.

Prawdopodobnie nigdy nie znalazłbym tego bez pomocy tej odpowiedzi i TooLargeTool .


1

Przeżyłem również TransactionTooLargeException. Po pierwsze, pracowałem nad zrozumieniem, gdzie to się dzieje. Wiem, dlaczego tak się dzieje. Każdy z nas wie z powodu dużej zawartości. Mój problem taki był i rozwiązałem. Może to rozwiązanie może być przydatne dla każdego. Mam aplikację, która pobiera zawartość z interfejsu API. Otrzymuję wynik z interfejsu API na pierwszym ekranie i przesyłam go na drugi ekran. Mogę pomyślnie wysłać tę zawartość na drugi ekran. Po drugim ekranie, jeśli chcę przejść na trzeci ekran, występuje ten wyjątek. Każdy mój ekran jest tworzony z Fragmentu. Zauważyłem to, kiedy wychodzę z drugiego ekranu. Zapisuje zawartość pakietu. jeśli ta zawartość jest zbyt duża, zdarza się ten wyjątek. Moje rozwiązanie jest po usunięciu zawartości z pakietu.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

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

Chociaż jest to słuszne (szkoda, zrozumiałem to samo po roku), co zrobi fragment, jeśli zostanie odtworzony (obrót ekranu)?
CoolMind

0

Rozwiązaniem byłoby zapisanie przez aplikację ArrayList (lub dowolnego obiektu powodującego problem) w systemie plików, a następnie przekazanie odwołania do tego pliku (np. Nazwa pliku / ścieżka) za pośrednictwem usługi Intent do usługi IntentService, a następnie pozostawienie usługi IntentService pobierz zawartość pliku i przekonwertuj go z powrotem na ArrayList.

Gdy usługa IntentService zrobi to z plikiem, powinna go usunąć lub przekazać instrukcję z powrotem do aplikacji za pośrednictwem transmisji lokalnej, aby usunąć utworzony plik (przekazując to samo odwołanie do pliku, które zostało do niego dostarczone).

Aby uzyskać więcej informacji, zobacz moją odpowiedź na ten związany problem .


0

Ponieważ intencje, dostawcy treści, komunikator, wszystkie usługi systemowe, takie jak telefon, wibrator itp., Wykorzystują dostawcę infrastruktury IPC firmy Binder. Co więcej, wywołania zwrotne cyklu życia aktywności również korzystają z tej infrastruktury.

1 MB to ogólny limit wszystkich transakcji segregatorów przeprowadzonych w systemie w danym momencie.

W przypadku gdy wiele transakcji dzieje się w momencie wysłania zamiaru, może się to nie udać, nawet jeśli dodatkowe dane nie są duże. http://codetheory.in/an-overview-of-android-binder-framework/


0

Przy tak wielu miejscach, gdzie TransactionTooLargeException może happen-- oto jeszcze jeden nowy na Androida 8 - trzask, gdy ktoś po prostu zaczyna wpisywać się w EditText jeśli zawartość jest zbyt duży.

Jest to związane z AutoFillManager (nowość w API 26) i następującym kodem w StartSessionLocked():

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

Jeśli dobrze rozumiem, wywołuje to usługę autouzupełniania - przekazując AutofillManagerClient w segregatorze. A kiedy EditText ma dużo treści, wydaje się, że powoduje TTLE.

Kilka rzeczy może to złagodzić (lub zrobiłem tak, jak i tak testowałem): Dodaj android:importantForAutofill="noExcludeDescendants"w deklaracji układu xml EditText. Lub w kodzie:

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

Drugim okropnym, okropnym obejściem może być również zastąpienie metod performClick()i w onWindowFocusChanged()celu wychwycenia błędu w samej podklasie TextEdit. Ale nie sądzę, żeby to było naprawdę mądre ...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.