我得到了一个TransactionTooLargeException。无法复制的。文件里说

The Binder transaction failed because it was too large. During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown. ... There are two possible outcomes when a remote procedure call throws TransactionTooLargeException. Either the client was unable to send its request to the service (most likely if the arguments were too large to fit in the transaction buffer), or the service was unable to send its response back to the client (most likely if the return value was too large to fit in the transaction buffer). ...

在某个地方,我传递或接收的参数超出了未知的限制。在哪里?

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)

这似乎和观点有关?这与远程过程调用有什么关系?

可能重要的是:Android版本:4.0.3,设备:HTC One X


当前回答

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

发生的是我们在ViewPager中使用FragmentStatePagerAdapter。用户将翻页并创建100多个片段(这是一个阅读应用程序)。

虽然我们在destroyItem()中正确地管理片段,但在android中 FragmentStatePagerAdapter的实现有一个bug,它保持了对以下列表的引用:

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子类中的片段,基类仍然会存储一个片段。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;
}

其他回答

最近我在使用Android的Contacts Provider时遇到了一个有趣的情况。

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

由于它作为一个单独的应用程序工作-所有类型的数据传输都是通过使用Binder机制执行的,因此Binder缓冲区在这里发挥作用。

我的主要错误是我没有关闭游标与blob数据从接触提供者,因此分配给提供者的内存增加,这膨胀的Binder缓冲区,直到我得到吨!!绑定器事务失败!!消息在我的LogCat输出。

因此,主要的思想是,当你使用外部内容提供程序并从它们获得游标时,当你完成使用它们时,总是关闭它。

一个解决方案是应用程序将数组列表(或任何导致问题的对象)写入文件系统,然后通过Intent将该文件的引用(例如,文件名/路径)传递给IntentService,然后让IntentService检索文件内容并将其转换回数组列表。

当IntentService处理完文件后,它应该要么删除它,要么通过Local Broadcast将指令传递回应用程序以删除它创建的文件(传递回提供给它的相同文件引用)。

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

将此添加到您的活动中

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

对我有用,希望对你也有帮助

对我来说,TransactionTooLargeException发生时,我试图发送大位图图像从一个活动到另一个通过意图。我通过使用应用程序的全局变量解决了这个问题。

例如,如果你想从活动A发送一个大的位图图像到活动B,那么将这个位图图像存储在全局变量中

((Global) this.getApplication()).setBitmap(bitmap);

然后启动活动B并从全局变量中读取

Bitmap bitmap = ((Global) this.getApplication()).getBitmap();

我们的应用程序也有这个问题。经过测试,发现当应用程序内存不足,活动回收时,系统调用onSaveInstanceState方法保存大量的bundle数据,而且每次数据都变大,最后报错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;
}