如何处理TransactionTooLargeException

Ixx*_*Ixx 225 android exception

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

Binder事务失败,因为它太大了.

在远程过程调用期间,调用的参数和返回值将作为存储在Binder事务缓冲区中的Parcel对象传输.如果参数或返回值太大而不适合事务缓冲区,则调用将失败并抛出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)
Run Code Online (Sandbox Code Playgroud)

它似乎与视图有关,因为所有的Window/View行?这与远程过程调用有什么关系?我该如何查找此错误的原因?

在应用程序中我只使用Webservices,我没有使用Service类,Web服务是"远程过程调用"还是其他可能......?

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

Dur*_*amy 149

我遇到了这个问题,我发现当服务和应用程序之间交换大量数据时(这涉及传输大量缩略图).实际上数据大小约为500kb,IPC事务缓冲区大小设置为1024KB.我不确定为什么它超出了事务缓冲区.

当您通过intent extras传递大量数据时,也会发生这种情况

当您在应用程序中遇到此异常时,请分析您的代码.

  1. 您是在服务和应用程序之间交换大量数据吗?
  2. 使用意图来共享大量数据(例如,用户从图库共享新闻共享中选择大量文件,将使用意图传输所选文件的URI)
  3. 从服务接收位图文件
  4. 等待android用大量数据回复(例如,当用户安装了大量应用程序时,getInstalledApplications())
  5. 使用applyBatch(),其中有许多操作待定

当你得到这个例外时如何处理

如果可能,将大操作拆分为小块,例如,而不是使用1000次操作调用applyBatch(),每次调用100.

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

我不知道该怎么做,但是,不要查询android,它可以返回大量数据:-)

  • 我在调用`getInstalledApplications`时正好得到了这个异常.可以做些什么来解决这个问题? (14认同)
  • 如果您发现很难确切地追踪导致崩溃的状态,那么您可能会发现[TooLargeTool](https://github.com/guardian/toolargetool)非常有用. (8认同)
  • 我可以确认你关于限制大约500KB的结论,但它是特定于设备的,在某些设备上你可以传输几乎整个1MB.我也有这个例外,所以我做了一些调查,写了一篇文章,对于遇到这个问题的人来说可能很有趣.http://nemanjakovacevic.net/blog/english/2015/03/24/yet-another-post-on-serializable-vs-parcelable/ (7认同)

mvd*_*vds 40

这不是一个明确的答案,但它可能会对a的原因有所了解TransactionTooLargeException并帮助查明问题.

虽然大多数答案都涉及大量传输的数据,但我看到在重度滚动和缩放以及重复打开ActionBar微调器菜单后偶然抛出此异常.点击操作栏时会发生崩溃.(这是一个自定义地图应用程序)

传递的唯一数据似乎是从"输入调度程序"到应用程序的触摸.我认为在"事务缓冲区"中这不可能合理地接近1 mb.

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

崩溃总是紧接着这些行:

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 !!!
Run Code Online (Sandbox Code Playgroud)

哪个不是按顺序打印的,但是(据我检查)在同一毫秒内发生.

为清楚起见,堆栈跟踪本身与问题中的相同:

E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException
Run Code Online (Sandbox Code Playgroud)

深入研究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);
Run Code Online (Sandbox Code Playgroud)

对我而言,听起来我可能正在使用这个未记录的功能,其中交易因其他原因而失败,而不是事务是TooLarge.他们应该命名它TransactionTooLargeOrAnotherReasonException.

这时我没有解决问题,但如果我发现有用的东西,我会更新这个答案.

更新:事实证明我的代码泄漏了一些文件描述符,其数量在linux中最大化(通常为1024),这似乎触发了异常.毕竟这是一个资源问题.我通过打开/dev/zero1024次验证了这一点,这导致了UI相关操作中的各种奇怪异常,包括上面的异常,甚至一些SIGSEGV.显然无法打开文件/套接字并不是在Android中非常干净地处理/报告的内容.


sul*_*lai 34

如果您需要调查导致崩溃的Parcel,您应该考虑尝试使用TooLargeTool.

(我发现这是@Max Spencer根据接受的答案发表的评论,这对我的情况很有帮助.)

  • 最被低估的解决方案.此工具可帮助您缩小违规活动的范围 (6认同)
  • 使用此工具,我检测到我在片段中使用了巨大的捆绑包。我所做的是从包中提取参数并使用“bundle.clear()”清除包 (3认同)
  • @maxwellnewage:它也适用于java (2认同)
  • @maxwellnewage:似乎最新版本(0.2.1,0.2.0)目前在仅 Java 应用程序中[无法工作](https://github.com/guardian/toolargetool/issues/29)。我不得不使用 0.1.6 版本,当时效果很好 (2认同)

小智 33

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

发生的事情是我们正在使用FragmentStatePagerAdaptera ViewPager.用户可以翻阅并创建100多个片段(它是一个阅读应用程序).

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

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
Run Code Online (Sandbox Code Playgroud)

当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;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,即使您正确地管理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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我在使用 ViewPager2 和 FragmentStateAdapter 时遇到了同样的问题,这里的问题是 FragmentStateAdapter 标记为“saveState()”为最终类,并且 ViewPager2 类也是最终类,因此我们无法重写这些方法。但是,我能够通过在视图寻呼机 &lt;androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false" android:id= 的 XML 条目中添加 \android:saveEnabled="false" ' 来解决 Transaction Too Large 问题“@+id/viewPager” android:layout_width="fill_parent" android:layout_height="fill_parent" /&gt; (7认同)
  • `@Override public Parcelable saveState() { Bundle bundle = (Bundle) super.saveState(); if (bundle != null) { Parcelable[] states = bundle.getParcelableArray("states"); // 子集仅最后 3 个状态 if (states != null) states = Arrays.copyOfRange(states, states.length &gt; 3 ? states.length - 3 : 0, states.length - 1); bundle.putParcelableArray("states", states); } else bundle = new Bundle(); 返回包;}` (2认同)

Yaz*_*006 19

对于那些因为查找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)
Run Code Online (Sandbox Code Playgroud)

但是在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) 
Run Code Online (Sandbox Code Playgroud)

该怎么办?

将数据保存在本地数据库中,并仅将id保存在实例状态,您可以使用该状态来检索此数据.


ojo*_*ifu 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;
}
Run Code Online (Sandbox Code Playgroud)

而不是这个:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);
Run Code Online (Sandbox Code Playgroud)


Vai*_*den 11

将应用程序发送到后台时,通常会抛出此异常.

所以我决定使用数据Fragment方法来完全规避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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的主要Activity上完全绕过保存的实例循环,并将respoinsibilty推迟到我的数据Fragment.无需在Fragments本身上使用它,sice将其状态自动添加到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
}
Run Code Online (Sandbox Code Playgroud)

剩下的就是弹出保存的实例:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(SavedInstanceFragment.getInstance(getFragmentManager()).popData());
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState( SavedInstanceFragment.getInstance( getFragmentManager() ).popData() );
}
Run Code Online (Sandbox Code Playgroud)

详细信息:http://www.devsbedevin.com/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


Sid*_*rth 9

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

  1. 你有加载和占用太多内存的位图,使用缩小尺寸
  2. 你在drawable-_dpi文件夹中缺少一些drawables,android在drawable中查找它们,并调整它们的大小,使你的setContentView突然跳转并使用大量内存.

使用DDMS并在播放应用程序时查看堆,这将为您提供有关setcontentview创建问题的一些指示.

我复制了所有文件夹中的所有drawables以摆脱问题2.

问题已解决.

  • 对于信息而言,这只是一个可怕的例外,因为它没有提供任何关于问题来源的线索. (10认同)

3c7*_*c71 8

重要的是要了解事务缓冲区限制为1 MB,无论设备功能或应用程序如何.此缓冲区用于您进行的每个API调用,并在应用程序当前正在运行的所有事务中共享.

我相信它也有一些像parcels这样的特定对象(Parcel.obtain()),所以始终将每个对象obtain()与a 匹配很重要recycle().

即使返回的数据小于1 MB(如果其他事务仍在运行),返回大量数据的API调用也很容易发生此错误.

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

然而,呼叫可能仍然失败,因此用a围绕它catch并且必要时能够重试是很重要的.

据我所知,除了重试并确保尽可能少地检索信息之外,没有解决此类问题的方法.


小智 8

这可能会发生,因为活动“A”可能有片段,并且当您导航到活动“B”时。

那么活动“A”的活动生命周期将是

OnResume->OnPause()->OnSavedInsanceState()

OnSavedInsanceState 中的此处可能会导致崩溃,因为它无法保存太多数据的状态。因此,尝试通过添加以下代码来清除活动“A”的 saveInsatnce 。

 @Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
    super.onSaveInstanceState(oldInstanceState);
    if (oldInstanceState != null) {
        oldInstanceState.clear();
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 8

我们的应用程序也有这个问题。经过测试发现,当应用内存不足,activity被回收时,系统调用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;
}
Run Code Online (Sandbox Code Playgroud)


Mak*_*vin 7

将此添加到您的活动

@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
    super.onSaveInstanceState(oldInstanceState);
    oldInstanceState.clear();
}
Run Code Online (Sandbox Code Playgroud)

它对我有用,希望对您也有帮助

  • 我认为,这是最有害的提示。为什么要清除要在onCreate()中还原的数据? (5认同)
  • 这段代码的含义是您最终不会保存实例状态...... (2认同)