Что делать на 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 КБ. Я не уверен, почему он превысил буфер транзакций.
Это также может произойти, когда вы передаете много данных через преднамеренные дополнения
Когда вы получите это исключение в вашем приложении, пожалуйста, проанализируйте ваш код.
- Вы обмениваетесь большим количеством данных между вашими сервисами и приложением?
- Используя намерения для обмена большими данными (например, пользователь выбирает огромное количество файлов из общего ресурса прессы галереи, URI выбранных файлов будут переданы с использованием намерений)
- получение растровых файлов из сервиса
- ожидание ответа Android с огромными данными (например, getInstalledApplications(), когда пользователь установил много приложений)
- использование 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 основных причины,
- у вас есть растровые изображения, которые загружают и занимают слишком много памяти, используйте сокращение
- В папках 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 под тегом приложения.
Это решило проблему в моем случае!