Що робити з 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


Ні. Але я її знову не отримав. Запропонуйте програму відстеження помилок у прямому додатку, і отримали це лише один раз приблизно за 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МБ) між службами та додатками

Я не знаю, як це зробити, але, не запитувати андроїд, який може повернути величезні дані :-)


1
Припустимо, я перевіряю, чи встановлено .apk чи ні? під час встановлення ... Я отримую той самий виняток під час перевірки свого пакету com.test.installedornot.My .apk розмір більше 9 МБ, то в такому випадку як я буду керувати цим винятком?
DJhon

15
Цей виняток я отримую саме під час дзвінка до getInstalledApplications. Що можна зробити, щоб вирішити це?
Стен

1
@Stan Ця api є поширеною і широко використовується навколо програми Android. Цей виняток насправді хвилює мене, коли я використовую цю програму.
мирна пасія

7
Я можу підтвердити ваші висновки щодо обмеження розміру десь 500 КБ, але це конкретно для пристрою, на деяких пристроях ви можете передати майже цілий 1 МБ. У мене був і цей виняток, тому я провів кілька розслідувань і написав пост, який може бути цікавим для читання людям, які мають цю проблему. nemanjakovacevic.net/blog/english/2015/03/24/…
Неманья Ковачевич

11
Якщо вам важко відстежити, який саме стан викликає ваш збій, то вам може бути корисним TooLargeTool .
Макс Спенсер

48

Якщо вам потрібно розібратися, який посилок викликає ваш збій, варто спробувати TooLargeTool .

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


9
Найбільш недооцінене рішення. Цей інструмент допоможе вам звузити діяльність, яка порушує правопорушення
Кедар Паранджапе,

Цей інструмент, який допоміг вирішити мою проблему. Простий в установці та використанні.
Карлос

цей інструмент призначений для kotlin: / будь-яка альтернатива для Java?
maxwellnewage

2
@maxwellnewage: начебто остання версія (також 0.2.1, 0.2.0) наразі не працює в Java-програмах. Мені довелося скористатися версією 0.1.6, і тоді вона спрацювала добре
heisenberg

Використовуючи цей інструмент, я виявляю, що я використовував величезні розміри пучків у своїх фрагментах. Що я зробив - це витяг аргументу з групи та очищений пакет, використовуючи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

Розібравшись у вихідному коді андроїда, знаходять такі рядки:

фреймворки / 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);

Для мене це здається, що я, можливо, потрапляю на цю недокументовану функцію, коли транзакція не вдається з інших причин, ніж транзакція, що є TooLarge. Вони повинні були це назвати TransactionTooLargeOrAnotherReasonException.

На даний момент я не вирішив питання, але якщо знайду щось корисне, оновлю цю відповідь.

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


36

Зараз TransactionTooLargeExceptionнас болить вже близько 4 місяців, і ми нарешті вирішили це питання!

Те, що відбувалося, ми використовуємо FragmentStatePagerAdapterв a ViewPager. Користувач перегляне і створить 100+ фрагментів (це програма для читання).

Хоча ми належним чином керуємо фрагментами destroyItem(), в Androids реалізації 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;
}

найкраща відповідь для мене. Дуже дякую
pavel

З усіх можливостей для цього це для мене здавалося єдиним варіантом. Що зберігається в стані, що дозволило б йому досягти такого розміру? Якщо держава потрібна настільки сильно, що існує код для її збереження, як це не викликає інших проблем? Спасибі
Кенні

Те саме питання, що і @Kenny
Містер Кролик

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; }
Рамі Сабрі

Я просто дозволю лише останні три стани, перевірте код вище.
Рамі Сабрі

20

Тим, хто з гіркотою розчарувався в пошуку відповіді, чому з’являється TransactionTooLargeException, спробуйте перевірити, скільки інформації ви зберігаєте в екземплярі.

У компіляції / 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)

Але у компіляції / 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) 

Що робити?

Збережіть дані у локальній базі даних та збережіть лише ідентифікатори у тому випадку, у якому ви можете скористатися для отримання цих даних.


чи можна зберегти його як глобальний параметр і використовувати його пізніше?
Jitendra ramoliya

@Jitendraramoliya Так, можна. Це саме те, що я маю на увазі.
Yazon2006

13

Цей виняток, як правило, видається під час надсилання програми на задній план.

Тому я вирішив використовувати метод Fragment даних, щоб повністю обійти onSavedInstanceStaeжиттєвий цикл. Моє рішення також обробляє складні стану екземплярів і звільняє пам'ять якнайшвидше.

Спочатку я створив простий Фрагмент для зберігання даних:

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/


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()з a recycle().

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

Наприклад, PackageManager.getInstalledApplication()виклик повертає список усіх встановлених програм. Додавання конкретних прапорів дозволяє отримати багато зайвих даних. Це, швидше за все, не вдасться, тому рекомендується не отримувати зайвих даних та отримувати їх за допомогою програми.

Однак виклик все ж може не вдатися, тому важливо оточити його catchі мати можливість повторити спробу.

Наскільки я знаю, немає жодної проблеми з цим питанням, окрім повторного спроби та переконайтеся, що ви зможете отримати якомога менше інформації.


Дякую за інформацію, що цей буфер ділиться між усіма транзакціями в рамках програми!
Артем Мостяєв

1
Якщо так було, то чому це я отримую лише на телефонах Android 4.4 і ніде більше. Я думаю, що це більше помилок в 4.4, що я не можу зрозуміти, чому.
JPM

9

Я теж отримав це виняток на Samsung S3. Я підозрюю 2 першопричини,

  1. у вас є растрові карти, які завантажують і займають занадто багато пам'яті, використовуйте скорочення
  2. У папках dravable-_dpi відсутні деякі чернетки, андроїд шукає їх у форматі "dravable" та змінює їх розмір, завдяки чому ваш setContentView раптом стрибає та використовує багато пам'яті.

Використовуйте DDMS і дивіться на свою купу під час відтворення свого додатка, це дасть вам певну інформацію про те, який setcontentview створює проблему.

Я скопіював усі малюнки у всі папки, щоб позбутися проблеми 2.

Питання вирішено.


Винятки з пам'яті / растрових зображень зазвичай виглядають по-різному. Я вже бачив багато їх тестування на Android 2.x - 4.x, і винятки завжди виглядають по-різному. Але, хто знає, можливо, це також пов'язано, але специфічно для версій 4.x.
Ixx

10
Це просто жахливе виняток, що стосується інформації, оскільки вона не дає ніякого поняття, звідки виникає проблема.
Ixx

Я думаю, що кілька днів минуло, які ваші висновки?
Денні

8

Додайте це до своєї діяльності

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

Для мене це працює, сподіваюсь, що і вам допоможе


7
Я думаю, це найшкідливіший натяк. Чому слід очистити дані, які ми хочемо відновити onCreate()?
CoolMind


4

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


4

Ви очистили свій старий InstanceState від методу onSaveInstanceState , і він буде добре працювати. Я використовую FragmentStatePagerAdapter для свого переглядача, тому я тримаю нижче метод Перевизначення у своїй діяльності батьків для явного InstanceState.

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

Я знайшов це рішення звідси android.os.TransactionTooLargeException на Nougat


Дуже дякую.
Andrain

Дякую за свою відповідь, допоможіть мені
Jatin Patel

3

Нещодавно я також стикався з цікавим випадком, працюючи з провайдером контактів Android .

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

Оскільки вона працює як окрема програма - всі види передачі даних здійснюються за допомогою механізму Binder, і тут буфер Binder вступає в гру.

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

Тому основна ідея полягає в тому, що коли ви працюєте із зовнішніми постачальниками вмісту та отримуєте Cursorїх, завжди закривайте їх, коли закінчите працювати з ними.


3

Я зіткнувся з тією ж проблемою, коли я намагався надіслати растрову карту за допомогою намірів, і в той же час, коли це сталося, я склав додаток.

Як описано в цій статті, введіть опис посилання. Це відбувається, коли активність перебуває в процесі зупинки, це означає, що активність намагалася відправити збережені пакети стану в ОС для безпечного зберігання для відновлення пізніше (після зміни конфігурації або обробляти смерть), але один або кілька пакетів, які він надіслав, були занадто великими.

Я вирішив це через хак, змінивши onSaveInstanceState у своїй діяльності:

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

і коментувати дзвінок супер. Це брудний злом, але він працює чудово. Растрові файли успішно відправлені без збоїв. Сподіваюся, що це комусь допоможе.


2

У моєму випадку я отримую TransactionTooLargeException як вторинний збій після того, як рідна бібліотека вийшла з ладу з SIGSEGV. Про аварію рідної бібліотеки не повідомляється, тому я отримую лише TransactionTooLargeException.


2

Я отримав це в моєму синхроаптері при спробі групового введення великого 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));
}

2
Що робити, якщо інший не вдається? Вам потрібно зробити більше ділення, використовуючи петлю. Чи може бути спосіб отримати розмір транзакції та отримати максимальний розмір транзакції?
андроїд розробник

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

Переконайтеся, що ви не розміщуєте в об'єкті даних намірів великих розмірів. У моєму випадку я додавав розмір String 500k, а потім починав іншу діяльність. З цим винятком вона завжди провалювалася. Я уникав обміну даними між видами діяльності, використовуючи статичні змінні діяльності - вам не доведеться надсилати їх до Намір, а потім витягувати з нього.

Що у мене було:

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

Це дуже погана ідея. Використання статичних змінних таким чином є кодовим запахом. Крім того, спробуйте запустити цей код прапором "Не продовжуйте діяльність" і натисніть додому після переходу до PageWebView. Якщо ви знову відкриєте свою діяльність, ви, ймовірно, отримаєте збій, оскільки MainActivity буде мертвим на 100% з усіма його змінними.
Кирило Запилаєв

1

Коли я маю справу з WebViewв моєму додатку, це відбувається. Я думаю, що це пов'язано з addViewресурсами та інтерфейсом користувача. У своєму додатку я додаю код у WebViewActivityтакому вигляді нижче, тоді він працює добре:

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

1

Я знайшов першопричину цього (ми отримали як "додавання вікна не вдалося", так і витік дескриптора файлу, як говорить mvds).

Існує помилка в BitmapFactory.decodeFileDescriptor()Android 4.4. Це відбувається тільки коли inPurgeableі inInputShareableз BitmapOptionsвстановлюються true. Це спричиняє багато проблем у багатьох місцях взаємодії з файлами.

Зауважте, що метод також викликається від MediaStore.Images.Thumbnails.getThumbnail().

Ця проблема впливає на універсальний завантажувач зображень . Пікассо і Гліде, здається, не зачіпаються. https://github.com/nostra13/Android-Universal-Image-Loader/isissue/1020


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 буде кинуто.


1

Я отримав TransactionTooLargeException від помилки Stackoverflow в тесті Android Espresso. Коли я зняв фільтр Logcat для свого додатка, я знайшов слід журналу стека помилок stackoverflow.

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


1

Можна використовувати:

android:largeHeap="true"

в Android Manifest під тегом програми.

Це вирішило питання в моєму випадку!


У моєму випадку (через заклик onSaveInstantStateдо діяльності / фрагментів та збереження великих списків) це не допомогло.
CoolMind

1
Це вважається поганою практикою, оскільки ви не маєте справу з причиною збереження великої кількості даних. На нових пристроях він виходить з ладу, і ліміт значно менший (256 КБ). Дослідіть, чому ви спочатку зберігаєте партії та зменшіть її.
Тім Кіст

1

Також я зіткнувся з цією проблемою щодо передачі даних Bitmap від однієї діяльності до іншої, але я приймаю рішення, роблячи мої дані статичними даними, і це для мене ідеально працює

Перш за все:

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 .


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

Хоча це правильно (мені соромно, я зрозумів те саме через рік), як буде робити цей фрагмент, якщо він відтворить (обертання екрана)?
CoolMind

0

Рішення полягає в тому, щоб програма записувала ArrayList (або будь-який об'єкт, що викликає проблему) у файловій системі, а потім передає посилання на цей файл (наприклад, ім’я / шлях) через Намір до IntentService, а потім нехай IntentService отримати вміст файлу та перетворити його назад у ArrayList.

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

Для отримання додаткової інформації дивіться мою відповідь на цю пов'язану проблему .


0

Як Інтенти, Провайдери вмісту, Месенджер, усі системні послуги, такі як Телефон, Вібратор тощо, використовують Binder. Постачальник інфраструктури IPC використовує цю інфраструктуру.

1МБ - це загальний ліміт на всі транзакції біндера, виконані в системі в конкретний момент.

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


0

З такою великою кількістю місць , де TransactionTooLargeException може happen-- ось ще нове для 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);
}

Другим жахливим, жахливим вирішенням може бути також переосмислення performClick()та onWindowFocusChanged()способи виявлення помилки в самому підкласі TextEdit. Але я не думаю, що це дійсно мудро ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.