如何处理TransactionTooLargeException

我得到了一个应用程序TransactionTooLargeException的错误跟踪。 不可复制,以前从未有过。 它在文档中说

粘合剂交易失败,因为它太大了。

在远程过程调用期间,调用的参数和返回值将作为存储在Binder事务缓冲区中的Parcel对象进行传输。 如果参数或返回值太大而不能放入事务缓冲区,则调用将失败,并且将引发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) 

这似乎与观点相关,因为所有的窗口/视图线? 这与远程过程调用有什么关系? 我怎样才能find这个错误的原因?

在应用程序中,我只使用Web服务,我没有使用服务类,是Web服务的“远程过程调用”还是还有什么…?

提前致谢…

PS也许这很重要:Android版本:4.0.3,设备:HTC One X

我遇到了这个问题,并且发现在服务和应用程序之间交换大量数据时(这涉及到大量的缩略图)。 实际上数据大小约为500kb,IPC事务缓冲区大小设置为1024KB。 我不知道为什么它超过了事务缓冲区。

这也可能发生,当你通过意图的额外传递大量的数据

当您的应用程序中出现此exception时,请分析您的代码。

  1. 你是否在你的服务和应用程序之间交换大量的数据?
  2. 使用意图共享大量数据(例如,用户从图库共享新闻共享中select大量文件,所选文件的URI将使用意图进行传输)
  3. 从服务接收位图文件
  4. 等待android用巨大的数据回应(例如,当用户安装了很多应用程序时getInstalledApplications())
  5. 使用applyBatch()与很多操作挂起

当你得到这个exception时如何处理

如果可能的话,将大操作拆分成小块,而不是用1000个操作调用applyBatch(),每个调用100个。

不要在服务和应用程序之间交换大量的数据(> 1MB)

我不知道如何做到这一点,但是,不要查询Android,它可以返回巨大的数据:-)

这不是一个确定的答案,但它可能会揭示TransactionTooLargeException的原因,并帮助查明问题。

虽然大多数答案指的是大量的数据传输,但是在大量滚动和缩放以及反复打开ActionBar微调器菜单后,我发现这个exception是偶然抛出的。 碰撞发生在点击操作栏上。 (这是一个自定义的地图应用程序)

唯一传递的数据似乎是从“Input Dispatcher”到应用程序。 我认为这在“交易缓冲区”中不能合理地达到1 mb左右。

我的应用程序运行在四核1.6 GHz设备上,并使用3个线程进行重新分区,为UI线程保留一个内核。 此外,该应用程序使用android:largeHeap,剩下10MB未使用的堆,剩下100MB的空间来增长堆。 所以我不会说这是一个资源问题。

崩溃总是在这些行之前:

 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的源代码find这些行:

框架/碱/核心/ 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); 

对我来说,这听起来像我可能打这个未公开的function,其中交易失败的其他原因,而不是交易正在TooLarge。 他们应该把它命名为TransactionTooLargeOrAnotherReasonException

在这个时候,我没有解决问题,但如果我find一些有用的东西,我会更新这个答案。

更新:事实certificate,我的代码泄露了一些文件描述符,其数量在linux中最大化(通常为1024),这似乎触发了exception。 所以这毕竟是一个资源问题。 我通过打开/dev/zero 1024次validation了这一点,这导致了UI相关操作中的各种奇怪的exception,包括上面的exception,甚至一些SIGSEGV。 显然,无法打开文件/套接字不是很清楚地处理/报告整个Android。

TransactionTooLargeException一直困扰着我们大约4个月,我们终于解决了这个问题!

发生了什么事情是我们在ViewPager中使用FragmentStatePagerAdapter。 用户将通过并创build100多个片段(其阅读应用程序)。

尽pipe我们在destroyItem()中正确地pipe理了这些片段,但是在FragmentStatePagerAdapter的Androids实现中存在一个错误,它保留了对以下列表的引用:

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

如您所见,即使您正确pipe理FragmentStatePagerAdapter子类中的片段,基类仍将为创build的每个片段存储Fragment.SavedState。 TransactionTooLargeException发生时,该数组被转储到parcelableArray和操作系统不会喜欢它100 +项目。

因此,我们的修补程序是覆盖saveState()方法,不存储任何“状态”。

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

这个问题没有一个具体的原因。对我来说,在我的片段课上,我是这样做的:

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

对于那些为寻找TransactionTooLargeException apears的答案而苦苦失望的人, 试着检查一下你保存的实例状态。

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

该怎么办?

将数据保存在本地数据库中,并且只保留id的实例状态,您可以使用它来检索此数据。

我也在三星S3上得到了这个例外。 我怀疑有两个根本原因,

  1. 你有位图加载和占用太多的内存,使用缩小
  2. 你可以从drawable-_dpi文件夹中find一些drawables,android会以drawable方式查找它们,并调整它们的大小,使得你的setContentView突然跳转并使用大量的内存。

使用DDMS,当你玩你的应用程序,看看你的堆,这会给你一些setcontentview创build问题的迹象。

我复制了所有文件夹中的所有可绘图以摆脱问题2。

我会在几天内报告我find的。

无论设备function或应用程序如何,事务缓冲区限制为1 MB是很重要的。 此缓冲区与您所做的每个API调用一起使用,并在应用程序当前正在运行的所有事务中共享。

我相信它也包含一些特定的对象,例如(Parcel.obtain())(Parcel.obtain()) ,所以总是将每个(Parcel.obtain()) obtain()recycle()匹配起来非常重要。

即使返回的数据小于1 MB(如果其他事务仍在运行),也会在返回大量数据的API调用中轻松地发生此错误。

例如, PackageManager.getInstalledApplication()调用返回所有已安装应用程序的列表。 添加特定的标志允许检索大量额外的数据。 这样做可能会失败,因此build议不要检索任何额外的数据,并根据每个应用程序检索这些数据。

然而,这个呼叫可能仍然失败,所以重要的是围绕它捕捉并能够在必要时重试。

据我所知,除了重试和确保尽可能less的信息检索之外,没有解决这个问题的办法。

所以对于我们来说,是我们试图通过我们的AIDL接口发送一个太大的对象到远程服务。 交易大小不能超过1MB。 请求被分解成512KB的单独的块,并通过接口一次发送一个。 我知道一个残酷的解决scheme,但嘿 – 它的Android 🙁

最近我也遇到了一个有趣的案例,同时使用Android的联系人提供程序

我需要从内部联系人数据库加载联系人的照片,并根据系统架构,所有这些数据都通过查询传送给联系人提供程序。

由于它作为一个单独的应用程序工作 – 所有types的数据传输都使用Binder机制来执行,所以Binder缓冲区在这里起作用。

我的主要错误是, 我没有closures从联系人提供程序获取的blob数据的Cursor ,以便为提供程序分配的内存增加,这充气的Binder缓冲区,直到我吨吨!!!FAILED BINDER TRANSACTION!!!的粘合剂!!!FAILED BINDER TRANSACTION!!! 消息在我的LogCat输出。

所以主要的想法是,当你与外部的内容提供商合作,并从他们那里得到Cursor时,当你完成与他们合作时,一定要closures它。

在我的情况下,本机库与SIGSEGV崩溃后,我得到TransactionTooLargeException作为二次崩溃。 本机库崩溃没有报告,所以我只收到TransactionTooLargeException。

另一个可能原因:

我有一个在onResume()开始的活动! 这导致了大量的交易,导致手机(Galaxy S2)完全冻结(没有ANR或任何东西),然后硬重置,这本身就是一个巨大的bug。

用这个代码看看在其他手机上会发生什么会很有趣:

 class MyActivity extends Activity { // ... @Override void onResume() { super.onResume() startActivity(new Intent(this, MyActivity.class)); } } 

确保不要放入大尺寸的Intent对象数据。 在我的情况下,我添加string500K大小,然后开始另一个活动。 这个例外总是失败。 我通过使用活动的静态variables来避免在活动之间共享数据 – 您不必将它们发送给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); 

我发现这个根本原因(我们得到了“添加窗口失败”和文件描述符泄漏mvds说)。

Android 4.4的BitmapFactory.decodeFileDescriptor()中存在一个错误。 只有在将BitmapOptions的inPurgeable和inInputShareable设置为true时才会发生。 这导致许多地方与文件交互的许多问题。 https://code.google.com/p/android/issues/detail?id=65638

请注意,该方法也从MediaStore.Images.Thumbnails.getThumbnail()调用。

Universal Image Loader受此问题影响。 毕加索和滑翔似乎没有受到影响。

https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020

当我尝试批量插入一个大的ContentValues []时,我在我的syncadapter中得到了这个。 我决定修改它如下:

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

这个exception通常在应用程序被发送到后台时抛出。

所以我决定使用数据片段方法来完全规避onSavedInstanceStae生命周期。 我的解决scheme还处理复杂的实例状态并尽快释放内存。

首先,我创build了一个简单的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; } } 

然后,在我的主要活动中,我彻底规避了已保存的实例周期,并将respoinsibilty推迟到我的数据片段。 没有必要在碎片本身上使用它,它们的状态自动添加到Activity的状态):

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

剩下的只是popup保存的实例:

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

当我正在处理WebViewin我的应用程序,它发生。我认为这是相关的'addView'和UI资源。 在我的应用程序中,我在下面的WebViewActivity中添加一些代码,它运行正常:

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

解决scheme是应用程序将ArrayList(或导致问题的任何对象)写入文件系统,然后通过Intent向IntentService传递对该文件的引用(例如文件名/path),然后让IntentService检索文件内容并将其转换回ArrayList。

当IntentService处理完文件后,它应该删除它,或者通过本地广播将指令传回给应用程序,以删除它创build的文件(传回提供给它的相同文件引用)。

欲了解更多信息,请参阅我对这个相关问题的答案

由于Intents,Content Provider,Messenger,电话,振动器等所有系统服务都使用Binder的IPC基础设施提供程序,而且活动生命周期callback也使用这个基础设施。

1MB是系统在特定时刻执行的所有绑定事务的总体限制。

如果在发送意图时发生大量事务,即使额外的数据量不大,也可能会失败。 http://codetheory.in/an-overview-of-android-binder-framework/

对我来说也是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的位置并在覆盖的onSaveInstanceState()方法中调用adapter.removeFragments():

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

最后,在覆盖的onResume()方法中,如果适配器不为null,则重新实例化该适配器。 (如果它是空的,那么Activity就是第一次被打开,或者在Android被杀掉之后,onCreate将会创build适配器。)

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

我面临同样的问题,并find解决办法。 我通过意向附加程序传递了大量数据:

 Intent intent = new Intent(MainActivity.this, SecondActivity.class); 

而不是intent.putExtra(String name, String value); ,只是使用

 SecondActivity.getValue = value; 

接着

 startActivity(intent); 

getValue是SecondActivity在这里声明的静态variables。