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

239

Я получил TransactionTooLargeException. Не воспроизводится. В документах сказано

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

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

...

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

...

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

Трассировка стека не показывает ничего полезного:

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)

Кажется, это связано с мнением? Как это связано с удаленным вызовом процедуры?

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

Ixx
источник
Но я не получил это снова. Имейте трекер ошибок в живом приложении и получите его только один раз в 3 недели. По крайней мере, это не часто случается. Возможно, стоит открыть вопрос на Android, хотя ...
Ixx
У меня нет ответа, но это надежно приводит к полной перезагрузке моего Galaxy S2.
Тимммм
Я только что это произошло в одном из моих приложений сегодня. Это также произошло только один раз и с Galaxy S3. Интересно, что это кажется только для более мощных устройств.
danwms
это исключение было добавлено в API 15, developer.android.com/reference/android/os/… И я воспроизвел его в MapView, прокручивая карту. пока gc не написал, что у меня нет памяти. (Это заняло у меня пару минут)
meh
Размер буфера транзакций ограничен 1 МБ на всех устройствах, и этот буфер устаревает для каждой транзакции. Таким образом, чем мощнее устройство, тем больше транзакций оно может выполнять одновременно, причем все они потребляют один и тот же буфер 1 МБ. Тем не менее, ваш ответ не ответ, а комментарий.
3c71

Ответы:

158

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

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

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

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

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

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

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

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

Durairaj Packirisamy
источник
1
Предположим, я проверяю, установлен ли мой .apk или нет? во время установки ... Я получаю то же исключение при проверке моего пакета com.test.installedornot.Мой размер .apk больше 9 МБ, то в таком случае, как я буду управлять этим исключением?
DJhon
15
Я получаю это исключение именно во время звонка getInstalledApplications. Что можно сделать, чтобы решить эту проблему?
Стэн
1
@Stan Этот API-интерфейс распространен и широко используется в приложениях для Android. Это исключение действительно беспокоит меня, когда я использую этот API.
Peacepassion
7
Я могу подтвердить ваши выводы о том, что лимит составляет около 500 КБ, но это зависит от устройства, на некоторых устройствах вы можете передавать почти весь 1 МБ. У меня было и это исключение, поэтому я провел некоторое расследование и написал пост, который может быть интересным для людей, имеющих эту проблему. nemanjakovacevic.net/blog/english/2015/03/24/...
Неманья Ковачевич
11
Если вам трудно точно определить, какое состояние вызывает ваш сбой, то вам может пригодиться TooLargeTool .
Макс Спенсер
48

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

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

sulai
источник
9
Наиболее недооцененное решение. Этот инструмент поможет вам сузить оскорбительные действия
Kedar Paranjape
Это инструмент, который помог решить мою проблему. Прост в установке и использовании.
Карлос
этот инструмент для kotlin: / любая альтернатива для Java?
maxwellnewage
2
@maxwellnewage: похоже, что последняя версия (тоже 0.2.1, 0.2.0) в настоящее время не работает только в приложениях на Java. Я должен был использовать версию 0.1.6, и тогда она работала хорошо
Гейзенберг
Используя этот инструмент, я обнаружил, что я использовал огромные размеры связок в моих фрагментах. То , что я сделал это выписке аргумента из пучка и очистил пучок с помощьюbundle.clear()
EJ Chathuranga
41

Это не окончательный ответ, но он может пролить некоторый свет на причины 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/zero1024 раза, что привело к всевозможным странным исключениям в действиях, связанных с пользовательским интерфейсом, включая исключение выше и даже некоторые SIGSEGV. Очевидно, что неудача в открытии файла / сокета - это не то, что обрабатывается / сообщается очень аккуратно в Android.

MVDS
источник
36

TransactionTooLargeExceptionБыли преследующие нас в течение 4 месяцев, и мы наконец -то решен вопрос!

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

Несмотря на то, что мы должным образом управляем фрагментами destroyItem(), в реализации Android 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произойдет, когда этот массив будет выгружен в a, 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;
}
IK828
источник
лучший ответ для меня. Большое спасибо
павел
Из всех возможностей для этого для меня это казалось единственным вариантом. Что хранится в состоянии, которое позволило бы ему достичь этого размера? Если состояние нужно так сильно, что есть код для его сохранения, как это не вызывает других проблем? Спасибо
Кенни
Тот же вопрос, что и у @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; }
Рами Сабри
Я только позволил только последние 3 состояния, проверьте код выше.
Рами Сабри
20

Для тех, кто сильно разочарован в поисках ответа на вопрос, почему появляется 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) 

Что делать?

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

Yazon2006
источник
Можно ли сохранить его в качестве глобального параметра и использовать его позже?
Джитендра Рамолия
@Jitendraramoliya Да, вы можете. Это именно то, что я имею в виду.
Yazon2006
13

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

Поэтому я решил использовать метод фрагментирования данных, чтобы полностью обойти 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.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/

Vaiden
источник
1
Что произойдет, если активность будет уничтожена, когда приложение находится в фоновом режиме, и вы попытаетесь установить приоритетную активность?
Мастер Бедствия
@MasterDisaster в этом случае состояние не сохраняется, потому что процесс, который содержит фрагмент экземпляра, умер.
Вайден
Права, поэтому этот случай работает только с изменением конфигурации.
Мастер Бедствия
Он работает всякий раз, когда запускается ОС onSavedState(), что происходит во многих случаях. Изменение конфигурации одно. Переключение приложений и переход в фоновый режим - это другое. И это еще не все.
Вайден
1
Я думаю, что это решение должно быть расширено для сохранения данных из разных источников. Вероятно, HashMap с тегами в качестве ключей и связками в качестве значений ..
CoolMind
11

Нет конкретной причины этой проблемы. Для меня в моем классе 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);
Оджонугва Джуд Очалифу
источник
9

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

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

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

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

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

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

3c71
источник
Спасибо за информацию, этот буфер распределяется между всеми транзакциями в приложении!
Артем Мостяев
1
Если бы это было так, то почему я получаю только на телефонах Android 4.4, и нет, где еще. Я думаю, что это больше ошибка в 4.4, что я не могу понять, почему.
JPM
9

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

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

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

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

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

Сиддхарт
источник
Память / растровые исключения обычно выглядят иначе. Я уже видел, как многие из них тестируют с Android 2.x - 4.x, и исключения всегда выглядят по-разному. Но, кто знает, может быть, это также связано, но характерно для версий 4.x.
Ixx
10
Это просто ужасное исключение в отношении информации, поскольку она не дает никакого представления о том, откуда возникла проблема.
Ixx
Я думаю, что несколько дней прошли, каковы ваши выводы?
Денни
8

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

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

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

Makvin
источник
7
Я думаю, это самый вредный намек. Почему мы должны очистить данные, которые мы хотим восстановить onCreate()?
CoolMind
2
Последствия этого кода в том, что вы НЕ сохраняете свое состояние экземпляра ...
Джастин
4

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

Кевин Паркер
источник
4

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

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

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

варотария вайси
источник
Огромное спасибо.
Андрэйн
Спасибо, ваш ответ, помогите мне
Джатин Патель
3

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

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

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

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

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

Алекс Бонель
источник
3

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

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

Я решил это с помощью хака, переопределив onSaveInstanceState в моей деятельности:

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

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

анонимное
источник
2

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

Крис
источник
2

Я получил это в моем 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));
}
Pepijn
источник
2
Что если другой не получится? Вы должны сделать больше деления, используя цикл. Есть ли способ получить размер транзакции и получить максимальный размер транзакции?
разработчик Android
2

Для меня это было также 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);
    }
}
С Фитц
источник
1

Убедитесь, что вы не вводите в объект объекта «Данные» большого размера. В моем случае я добавил размер строки 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);
gladimdim
источник
Это очень плохая идея. Использование статических переменных таким образом является запахом кода. Также попробуйте запустить этот код с флагом «Не выполнять действия» и нажать «Домой» после перехода к PageWebView. После того, как вы снова откроете свою деятельность, вы, вероятно, получите сбой, так как MainActivity будет мертвым на 100% со всеми его переменными.
Кирилл Запылаев,
1

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

@Override
protected void onDestroy() {
    if (mWebView != null) {
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);  
        mWebView.removeAllViews();  
        mWebView.destroy();
    }
    super.onDestroy();
}
cuixbo
источник
1

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

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

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

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

ypresto
источник
1

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

dest=Parcel.obtain(); 

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

Дилип
источник
1

Попробуйте использовать EventBusили ContentProviderкак решение.

Если вы находитесь в одном и том же процессе (как правило, все ваши действия будут), попробуйте использовать EventBus, потому что в процессе обмена данными НЕ требуется какой-то буфер, поэтому вам не нужно беспокоиться о том, что ваши данные слишком велики. (Вы можете просто использовать вызов метода, чтобы действительно передать данные, а EventBus скрыть ужасные вещи) Вот деталь:

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

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

Если две стороны намерения не находятся в одном и том же процессе, попробуйте несколько ContentProvider.


Смотрите TransactionTooLargeException

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

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

boileryao
источник
1

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

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

Дагмар
источник
1

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

android:largeHeap="true"

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

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

MazRoid
источник
В моем случае (из-за вызова onSaveInstantStateдействий / фрагментов и сохранения больших списков) это не помогло.
CoolMind
1
Это считается плохой практикой, потому что вы не имеете дела с причиной, по которой в первую очередь сохраняется много данных. На более новых устройствах происходит сбой приложения, и ограничение намного меньше (256 КБ). Сначала выясните, почему вы храните партии, и уменьшите их.
Тим Кист
1

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

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

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;
}
Басант
источник
1

Это происходило в моем приложении, потому что я передавал список результатов поиска в аргументах фрагмента, назначая этот список свойству фрагмента - которое фактически является ссылкой на то же место в памяти, на которое указывают аргументы фрагмента, - затем добавляя новые элементы в списке, которые также изменили размер аргументов фрагмента. Когда действие приостанавливается, базовый класс фрагмента пытается сохранить аргументы фрагмента в 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 .

Big McLargeHuge
источник
1

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

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }
nebyan
источник
Хотя это правильно (позор, я понял то же самое через год), как будет работать фрагмент, если он воссоздается (поворот экрана)?
CoolMind
0

Решением может быть приложение для записи ArrayList (или любого объекта, вызывающего проблему) в файловую систему, затем передача ссылки на этот файл (например, имя файла / путь) через Intent в IntentService и затем возможность IntentService. получить содержимое файла и преобразовать его обратно в ArrayList.

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

Для получения дополнительной информации см. Мой ответ на эту проблему .

запрет-геоинженерия
источник
0

В качестве Intents, контент-провайдеры, Messenger, все системные службы, такие как телефон, вибратор и т. Д., Используют провайдера инфраструктуры IPC от Binder. Более того, обратные вызовы жизненного цикла активности также используют эту инфраструктуру.

1 МБ - это общий лимит для всех транзакций связывания, выполняемых в системе в определенный момент.

Если при отправке намерения происходит много транзакций, он может завершиться неудачей, даже если дополнительные данные невелики. http://codetheory.in/an-overview-of-android-binder-framework/

Шиноо Гоял
источник
0

В очень многих местах, где может произойти TransactionTooLargeException - вот еще одно новшество в Android 8 - сбой, когда кто-то просто начинает печатать в EditText, если содержимое слишком велико.

Это связано с AutoFillManager (новым в API 26) и следующим кодом в StartSessionLocked():

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

Если я правильно понимаю, это вызывает службу автозаполнения - передавая AutofillManagerClient в связывателе. И когда EditText имеет много контента, он, похоже, вызывает TTLE.

Несколько вещей могут смягчить это (или сделали так, как я тестировал в любом случае): Добавьте android:importantForAutofill="noExcludeDescendants"в объявление макета EditText xml. Или в коде:

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

2 - ая ужасный, ужасный обходной путь может быть также переопределить performClick()и onWindowFocusChanged()методы , чтобы поймать ошибку в самом TextEdit подкласса. Но я не думаю, что это действительно мудро ...

fattire
источник