Что делать на TransactionTooLargeException

Я попал в трек ошибки приложения TransactionTooLargeException. Не воспроизводится и никогда не было раньше. В документах сказано

Сбой транзакции Binder, поскольку она была слишком большой.

Во время удаленного вызова процедуры аргументы и возвращаемое значение вызова передаются как объекты Parcel, хранящиеся в буфере транзакций Binder. Если аргументы или возвращаемое значение слишком велики, чтобы поместиться в буфере транзакций, вызов завершится неудачно и будет выдано исключение TransactionTooLargeException.

...

Есть два возможных результата, когда удаленный вызов процедуры создает исключение TransactionTooLargeException. Либо клиент не смог отправить свой запрос в службу (скорее всего, если аргументы были слишком велики для размещения в буфере транзакций), либо службе не удалось отправить свой ответ обратно клиенту (скорее всего, если возвращаемое значение было слишком большой, чтобы поместиться в буфере транзакций).

...

Итак, где-то я передаю или получаю аргументы, которые превышают какой-то неизвестный предел. Но где?

Stacktrace ничего не показывает из моих файлов:

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)

Кажется, это связано с представлениями, потому что все строки Window / View? Как это связано с удаленным вызовом процедуры? Как я могу найти причину этой ошибки?

В приложении я использую только веб-сервисы, я не использую класс Service, являются ли веб-сервисы "удаленными вызовами процедур" или что еще может быть...?

PS Может это важно: версия Android: 4.0.3, устройство: HTC One X

47 ответов

Решение

Я столкнулся с этой проблемой и обнаружил, что при обмене огромным объемом данных между службой и приложением (это включает в себя передачу большого количества миниатюр). На самом деле размер данных составлял около 500 КБ, а размер буфера транзакции IPC был равен 1024 КБ. Я не уверен, почему он превысил буфер транзакций.

Это также может произойти, когда вы передаете много данных через преднамеренные дополнения

Когда вы получите это исключение в вашем приложении, пожалуйста, проанализируйте ваш код.

  1. Вы обмениваетесь большим количеством данных между вашими сервисами и приложением?
  2. Используя намерения для обмена большими данными (например, пользователь выбирает огромное количество файлов из общего ресурса прессы галереи, URI выбранных файлов будут переданы с использованием намерений)
  3. получение растровых файлов из сервиса
  4. ожидание ответа Android с огромными данными (например, getInstalledApplications(), когда пользователь установил много приложений)
  5. использование applyBatch() с большим количеством ожидающих операций

Как справиться, когда вы получите это исключение

Если возможно, разделите большую операцию на маленькие порции, например, вместо того, чтобы вызывать applyBatch() с 1000 операциями, вызывайте ее с 100 каждой.

Не обменивайтесь огромными данными (>1 МБ) между службами и приложением

Я не знаю, как это сделать, но, не запрашивайте Android, который может вернуть огромные данные:-)

Если вам нужно выяснить, какая посылка вызывает сбой, попробуйте TooLargeTool.

(Я нашел это как комментарий @Max Spencer под принятым ответом, и это было полезно в моем случае.)

Это не окончательный ответ, но он может пролить свет на причины TransactionTooLargeException и помочь точно определить проблему.

Хотя большинство ответов относятся к большим объемам передаваемых данных, я вижу, что это исключение возникает случайно после интенсивной прокрутки и масштабирования и многократного открытия вращающегося меню ActionBar. Сбой происходит при нажатии на панель действий. (это пользовательское картографическое приложение)

Похоже, единственные передаваемые данные касаются приложения "Диспетчер ввода" и приложения. Я думаю, что это не может разумно составить где-то около 1 МБ в "Буфере транзакций".

Мое приложение работает на четырехъядерном устройстве с частотой 1,6 ГГц и использует 3 потока для тяжелой работы, оставляя одно ядро ​​свободным для потока пользовательского интерфейса. Кроме того, приложение использует android: largeHeap, у него осталось 10 МБ неиспользуемой кучи и осталось 100 МБ места для роста кучи. Так что я бы не сказал, что это проблема с ресурсами.

Сбою всегда сразу предшествуют следующие строки:

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

Которые не обязательно печатаются в этом порядке, но (насколько я проверял) происходят в одну и ту же миллисекунду.

И сама трассировка стека, для ясности, такая же, как в вопросе:

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

Копаясь в исходном коде android, можно найти следующие строки:

рамки / база / ядро ​​/ 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);

Для меня это звучит так, как будто я использую эту недокументированную функцию, в которой транзакция завершается неудачей по другим причинам, а не транзакция TooLarge. Они должны были назвать это TransactionTooLargeOrAnotherReasonException,

На данный момент я не решил проблему, но если я найду что-то полезное, я обновлю этот ответ.

обновление: оказалось, что в моем коде просочились некоторые файловые дескрипторы, число которых максимально увеличено в linux (обычно 1024), и это, похоже, вызвало исключение. Так что это была проблема с ресурсом в конце концов. Я подтвердил это, открыв /dev/zero 1024 раза, что приводило к всевозможным странным исключениям в действиях, связанных с пользовательским интерфейсом, включая исключение выше и даже некоторые SIGSEGV. Очевидно, что неудача в открытии файла / сокета - это не то, что обрабатывается / сообщается очень аккуратно в Android.

TransactionTooLargeException преследует нас уже около 4 месяцев, и мы наконец решили проблему!

То, что происходило, было то, что мы используем FragmentStatePagerAdapter в ViewPager, Пользователь будет пролистывать и создавать более 100 фрагментов (это приложение для чтения).

Хотя мы правильно разбираем фрагменты в destroyItem(), в андроид реализации FragmentStatePagerAdapter есть ошибка, при которой хранится ссылка на следующий список:

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

И когда Android FragmentStatePagerAdapter пытается сохранить состояние, он вызовет функцию

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

Как видите, даже если вы правильно управляете фрагментами в FragmentStatePagerAdapter подкласс, базовый класс будет по-прежнему хранить Fragment.SavedState за каждый фрагмент, когда-либо созданный. TransactionTooLargeException произойдет, когда этот массив был сброшен в parcelableArray и ОС не понравилось бы более 100 пунктов.

Поэтому исправление для нас было переопределить saveState() метод и не хранить ничего для "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;
}

Для тех, кто сильно разочарован в поисках ответа на вопрос, почему появляется TransactionTooLargeException, попробуйте проверить, что вы сохраняете в состоянии экземпляра.

На compile /targetSdkVersion <= 23 у нас есть только внутреннее предупреждение о большом размере сохраненного состояния, но ничего не происходит:

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)

Но при compile /targetSdkVersion> = 24 мы имеем реальный сбой RuntimeException в этом случае:

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) 

Что делать?

Сохраняйте данные в локальной базе данных и сохраняйте только идентификаторы в состоянии экземпляра, которые вы можете использовать для извлечения этих данных.

Это исключение обычно генерируется, когда приложение отправляется в фоновый режим.

Поэтому я решил использовать метод данных фрагмента, чтобы полностью обойти onSavedInstanceStae жизненный цикл. Мое решение также обрабатывает сложные состояния экземпляров и освобождает память как можно скорее.

Сначала я создал простой Fargment для хранения данных:

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

Затем в своей основной Деятельности я полностью обхожу цикл сохраненных экземпляров и переношу ответственность на свой фрагмент данных. Нет необходимости использовать это на самих фрагментах, так как их состояние добавляется в состояние действия автоматически):

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

Осталось просто извлечь сохраненный экземпляр:

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

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

Полная информация: http://www.devsbedevin.com/avoiding-transactiontoolargeexception-on-android-nougat-and-up/

Нет конкретной причины этой проблемы. Для меня в моем классе Fragment я делал это:

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

вместо этого:

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

Важно понимать, что буфер транзакций ограничен 1 МБ независимо от возможностей устройства или приложения. Этот буфер используется для всех вызовов API, которые вы делаете, и используется всеми транзакциями, которые в данный момент выполняется приложением.

Я считаю, что он также содержит некоторые конкретные объекты, такие как посылки и тому подобное (Parcel.obtain())поэтому важно всегда соответствовать каждому obtain() с recycle(),

Эта ошибка может легко произойти при вызовах API, возвращающих много данных, даже если возвращаемые данные меньше 1 МБ (если все еще выполняются другие транзакции).

Например, PackageManager.getInstalledApplication() Вызов возвращает список всех установленных приложений. Добавление определенных флагов позволяет получить много дополнительных данных. Это может привести к сбою, поэтому рекомендуется не извлекать дополнительные данные и извлекать их отдельно для каждого приложения.

Тем не менее, вызов все равно может быть неудачным, поэтому важно окружить его catch и сможете повторить попытку, если это необходимо.

Насколько я знаю, для решения этой проблемы нет обходного пути, кроме повторных попыток и получения как можно меньшего количества информации.

Добавьте это в свою активность

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

Это работает для меня, надеюсь, это также поможет вам

Я тоже получил это исключение на Samsung S3. Я подозреваю 2 основных причины,

  1. у вас есть растровые изображения, которые загружают и занимают слишком много памяти, используйте сокращение
  2. В папках drawable-_dpi у вас отсутствуют некоторые drawables, android ищет их в drawable и изменяет их размеры, заставляя ваш setContentView внезапно перепрыгивать и использовать много памяти.

Используйте DDMS и просматривайте свою кучу во время воспроизведения приложения, что даст вам некоторое представление о том, какой setcontentview создает проблему.

Я скопировал все Drawables во всех папках, чтобы избавиться от проблемы 2.

Проблема решена.

Это может произойти из-за того, что действие «A» может иметь фрагменты, и когда вы переходите к действию «B».

тогда жизненный цикл активности "A" будет

OnResume->OnPause()->OnSavedInsanceState()

здесь, в OnSavedInsanceState, это может вызвать сбой, потому что он не может сохранить состояние с большим количеством данных. чтобы попытаться очистить saveInsatnce действия «A», добавив следующий код.

       @Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
    super.onSaveInstanceState(oldInstanceState);
    if (oldInstanceState != null) {
        oldInstanceState.clear();
    }

}

У нашего приложения тоже есть эта проблема. После тестирования было обнаружено, что, когда памяти приложения недостаточно и активность повторно используется, система вызывает метод onSaveInstanceState для сохранения большого количества данных пакета, и данные каждый раз становятся большими, и, наконец, будет сообщено об ошибке TransactionTooLargeException. , поэтому я думаю, что этот метод должен решить эту проблему.

      public final int MAX_BUNDLE_SIZE = 300;
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
     super.onSaveInstanceState(outState);
     long bundleSize = getBundleSize(outState);
     if (bundleSize > MAX_BUNDLE_SIZE * 1024) {
         outState.clear();
     }
}

private long getBundleSize(Bundle bundle) {
     long dataSize;
     Parcel obtain = Parcel.obtain();
     try {
         obtain.writeBundle(bundle);
         dataSize = obtain.dataSize();
     } finally {
        obtain.recycle();
     }
     return dataSize;
}

Вы очистили свой старый InstanceState из метода onSaveInstanceState, и он будет работать хорошо. Я использую FragmentStatePagerAdapter для моего viewpager, поэтому я оставляю ниже метод Override в моей родительской активности для очистки InstanceState.

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

Я нашел это решение здесь android.os.TransactionTooLargeException на Nougat

Таким образом, для нас это была попытка отправить слишком большой объект через наш интерфейс AIDL в удаленный сервис. Размер транзакции не может превышать 1 МБ. Запрос разбивается на отдельные блоки по 512 КБ и отправляется по одному через интерфейс. Брутальное решение, которое я знаю, но эй - его Android:(

Я столкнулся с той же проблемой, когда пытался отправить растровое изображение через Intent, и в то же время, когда это происходит, я складывал приложение.

Как это описано в этой статье, введите описание ссылки, здесь это происходит, когда активность находится в процессе остановки, это означает, что активность пыталась отправить свои сохраненные пакеты состояния в ОС системы для безопасного хранения для восстановления позже (после изменения конфигурации или обработать смерть), но один или несколько Пакетов были слишком большими.

Я решил это путем взлома, переопределив onSaveInstanceState в моей деятельности:

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

и комментируйте звоните супер Это грязный хак, но он работает отлично. Растр был успешно отправлен без сбоев. Надеюсь, это кому-нибудь поможет.

Убедитесь, что вы не вводите в объект объекта "Данные" большого размера. В моем случае я добавил размер строки 500k, а затем начал другое действие. Это всегда терпело неудачу с этим исключением. Я избегал делиться данными между действиями, используя статические переменные действий - вам не нужно отправлять их в Intent, а затем извлекать из них.

Что у меня было:

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

Недавно я также столкнулся с интересным случаем при работе с поставщиком контактов Android.

Мне нужно было загрузить фотографии контактов из внутренней базы данных контактов, и в соответствии с архитектурой системы все эти данные доставляются по запросам поставщику контактов.

Поскольку он работает как отдельное приложение - все виды передачи данных выполняются с использованием механизма Binder, поэтому здесь вступает в действие буфер Binder.

Моя главная ошибка заключалась в том, что я не закрыл Cursor с данными большого двоичного объекта, полученными от поставщика контактов, так что объем памяти, выделенной для поставщика, увеличился, и это привело к увеличению буфера Binder, пока я не получил тонны !!!FAILED BINDER TRANSACTION!!! сообщения в моем выходе LogCat.

Поэтому основная идея заключается в том, что при работе с внешними контент-провайдерами и CursorИз них всегда закрывайте его, когда закончите с ними работать.

Это происходило в моем приложении, потому что я передавал список результатов поиска в аргументах фрагмента, назначая этот список свойству фрагмента - которое фактически является ссылкой на то же место в памяти, на которое указывают аргументы фрагмента, - затем добавляя новые элементы в списке, которые также изменили размер аргументов фрагмента. Когда действие приостанавливается, базовый класс фрагмента пытается сохранить аргументы фрагмента в onSaveInstanceState, который вылетает, если аргументы превышают 1 МБ. Например:

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

Самым простым решением в этом случае было присвоение копии списка свойству фрагмента вместо назначения ссылки:

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

Еще лучшим решением было бы не передавать столько данных в аргументах.

Я, вероятно, никогда бы не нашел это без помощи этого ответа и TooLargeTool.

Для меня это было также FragmentStatePagerAdapterоднако первостепенное saveState() не работал. Вот как я это исправил:

При звонке FragmentStatePagerAdapter конструктор, ведите отдельный список фрагментов внутри класса и добавьте метод для удаления фрагментов:

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

Тогда в Activity, спасти ViewPager положение и вызов adapter.removeFragments() в переопределенном onSaveInstanceState() метод:

private int pagerPosition;

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

Наконец, в переопределенном onResume() метод, повторно создать экземпляр адаптера, если это не так null, (Если это nullтогда Activity открывается в первый раз или после того, как приложение было убито Android, в котором onCreate сделает создание адаптера.)

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

Я также жил TransactionTooLargeException. Во-первых, я работал, чтобы понять, где это происходит. Я знаю причину, почему это происходит. Каждый из нас знает из-за большого содержания. Моя проблема была такой, и я решил. Может быть, это решение может быть полезным для всех. У меня есть приложение, которое получает контент от API. Я получаю результат из API на первом экране и отправляю его на второй экран. Я могу отправить этот контент на второй экран в случае успеха. После второго экрана, если я хочу перейти на третий экран, возникает это исключение. Каждый мой экран создан из фрагмента. Я заметил, что когда ухожу со второго экрана. Сохраняет содержимое пакета. если этот контент слишком велик, это исключение происходит. Мое решение - после того, как я получил контент из пакета, я очищаю его.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

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

Я получил это в моем syncadapter, когда пытался навалом вставить большое ContentValues ​​[]. Я решил исправить это следующим образом:

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

В моем случае я получаю TransactionTooLargeException в качестве вторичного сбоя после сбоя собственной библиотеки с SIGSEGV. Собственный сбой библиотеки не сообщается, поэтому я получаю только TransactionTooLargeException.

Вопрос решается:

 Bundle bundle = new Bundle();
  bundle.putSerializable("data", bigdata);
...
  CacheHelper.saveState(bundle,"DATA");
  Intent intent = new Intent(mActivity, AActivity.class);
  startActivity(intent, bb);// do not put data to intent.

In Activity:
   @Override
   protected void onCreate(Bundle savedInstanceState) {
        Bundle bundle = CacheHelper.getInstance().loadState(Constants.DATA);
        if (bundle != null){
            Intent intent = getIntent();
            intent.putExtras(bundle);
        }
        getIntent().getExtra(..);
        ....
   }
   @Override
    protected void onDestroy() {
        super.onDestroy();
        CacheHelper.clearState("DATA");
    }

public class CacheHelper {

    public static void saveState(Bundle savedInstanceState, String name) {
        Bundle saved = (Bundle) savedInstanceState.clone();
        save(name, saved);
    }
    public Bundle loadState(String name) {

        Object object = load(name);
        if (object != null) {
            Bundle bundle = (Bundle) object;
            return bundle;
        }
        return null;
    }
    private static void save(String fileName, Bundle object) {
        try {
            String path = StorageUtils.getFullPath(fileName);
            File file = new File(path);
            if (file.exists()) {
                file.delete();
            }
            FileOutputStream fos = new FileOutputStream(path, false);

            Parcel p = Parcel.obtain(); //creating empty parcel object
            object.writeToParcel(p, 0); //saving bundle as parcel
            //parcel.writeBundle(bundle);
            fos.write(p.marshall()); //writing parcel to file

            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static Bundle load(String fileName) {
        try {
            String path = StorageUtils.getFullPath(fileName);
            FileInputStream fis = new FileInputStream(path);

            byte[] array = new byte[(int) fis.getChannel().size()];
            fis.read(array, 0, array.length);

            Parcel parcel = Parcel.obtain(); //creating empty parcel object
            parcel.unmarshall(array, 0, array.length);
            parcel.setDataPosition(0);
            Bundle out = parcel.readBundle();
            out.putAll(out);

            fis.close();
            parcel.recycle();
            return out;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
public static void clearState(Activity ac) {
    String name = ac.getClass().getName();
    String path = StorageUtils.getFullPath(name);
    File file = new File(path);
    if (file.exists()) {
        file.delete();
    }
}
}

Эта одна строчка кода в методе writeToParcel(Parcel dest, int flags) помогла мне избавиться от TransactionTooLargeException.

dest=Parcel.obtain(); 

Только после этого кода я записываю все данные в объект посылки, т.е. dest.writeInt() и т. Д.

Я получил TransactionTooLargeException из-за ошибки Stackru в тесте Android Espresso. Я обнаружил трассировку стека ошибок переполнения стека в журналах, когда снял фильтр Logcat для своего приложения.

Я предполагаю, что Espresso вызвал TransactionTooLargeException при попытке обработать действительно большую трассировку стека исключений.

Я получил эту ошибку при удалении приложения, к которому другое приложение выполняло вызов IPC.

Гипотеза такова; поскольку «обслуживающее» приложение исчезло из-за удаления, некоторый буфер передачи мог быть заполнен, что вызвало эту ошибку, потому что ничего больше не «выталкивало» из буфера.

Если это имело место в вашем случае, эта проблема может не о чем беспокоиться.

Кроме того, я столкнулся с этой проблемой для передачи растровых данных от одного действия к другому, но я делаю решение, превращая мои данные в статические данные, и это прекрасно работает для меня

Сначала в деятельности:

public static Bitmap bitmap_image;

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

и во втором упражнении:

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

Когда я имею дело с WebView в моем приложении это происходит. Я думаю, что это связано с addView и ресурсы пользовательского интерфейса. В моем приложении я добавляю код в WebViewActivity как это ниже, то это работает нормально:

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

Я нашел основную причину этого (мы получили как "ошибка добавления окна", так и утечку файлового дескриптора, как говорит mvds).

В баге есть ошибка BitmapFactory.decodeFileDescriptor() Android 4.4. Это происходит только тогда, когда inPurgeable а также inInputShareable из BitmapOptions установлены в true, Это вызывает много проблем во многих местах взаимодействия с файлами.

Обратите внимание, что метод также вызывается из MediaStore.Images.Thumbnails.getThumbnail(),

Universal Image Loader подвержен этой проблеме. Пикассо и Глайд, похоже, не пострадали. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020

Можно использовать:

android:largeHeap="true"

в манифесте Android под тегом приложения.

Это решило проблему в моем случае!

Другие вопросы по тегам