286

我有一个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)

好像和观点有关?这与远程过程调用有何关系?

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

4

42 回答 42

186

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

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

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

  1. 您是否在服务和应用程序之间交换大量数据?
  2. 使用intents共享海量数据,(例如用户从gallery share press share中选择大量文件,选择文件的URI将使用intents传输)
  3. 从服务接收位图文件
  4. 等待 android 响应大量数据(例如,当用户安装大量应用程序时,getInstalledApplications())
  5. 使用 applyBatch() 有很多操作挂起

遇到此异常时如何处理

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

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

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

于 2012-10-09T22:10:26.073 回答
71

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

(我发现这是@Max Spencer 在接受的答案下的评论,对我来说很有帮助。)

于 2018-05-03T19:45:55.063 回答
46

这不是一个确定的答案,但它可能会阐明 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 !!!

不一定按该顺序打印,但(据我检查)发生在同一毫秒。

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

E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException

深入研究 android one 的源代码会发现以下几行:

框架/基础/核心/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 次验证了这一点,这导致 UI 相关操作中出现各种奇怪的异常,包括上面的异常,甚至是一些 SIGSEGV 的异常。显然,无法打开文件/套接字并不是在整个 Android 中非常干净地处理/报告的事情。

于 2013-01-17T12:22:16.653 回答
43

TransactionTooLargeException困扰我们4个月了,终于解决了!

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

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

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();

而当AndroidFragmentStatePagerAdapter试图保存状态时,它会调用该函数

@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 并且操作系统不喜欢它 100 多个项目时,就会发生这种情况parcelableArray

因此,我们的解决方法是覆盖该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;
}
于 2017-04-03T20:11:26.890 回答
24

对于那些在寻找 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) 

该怎么办?

将数据保存在本地数据库中,并仅将 id 保留在可用于检索此数据的实例状态中。

于 2016-11-29T17:24:28.577 回答
15

当应用程序被发送到后台时,通常会引发此异常。

所以我决定使用数据分片的方法来完全规避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;
    }
}

然后在我的主要活动中,我完全绕过保存的实例周期,并将责任推迟到我的数据片段。无需在 Fragments 本身上使用它,因为它们的状态会自动添加到 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
}

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

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

于 2017-08-28T12:24:41.770 回答
12

这个问题没有一个具体的原因。对我来说,在我的 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);
于 2015-10-04T10:46:59.213 回答
11

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

我相信它还包含一些特定的对象,例如 parcels 等(Parcel.obtain()),因此始终将 eachobtain()与 a匹配很重要recycle()

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

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

然而,调用可能仍然失败,所以用 a 包围它catch并在必要时能够重试非常重要。

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

于 2013-11-23T16:27:24.490 回答
9

我在三星 S3 上也遇到了这个异常。我怀疑有两个根本原因,

  1. 您有加载并占用过多内存的位图,请使用缩小尺寸
  2. 您在 drawable-_dpi 文件夹中缺少一些可绘制对象,android 在可绘制对象中查找它们并调整它们的大小,使您的 setContentView 突然跳转并使用大量内存。

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

我复制了所有文件夹中的所有可绘制对象以解决问题 2。

问题已解决。

于 2012-08-17T13:03:10.503 回答
8

将此添加到您的活动中

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

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

于 2018-11-05T12:18:40.050 回答
4

所以对我们来说,我们试图通过我们的 AIDL 接口将太大的对象发送到远程服务。事务大小不能超过 1MB。请求被分解为 512KB 的单独块,并通过接口一次发送一个。我知道一个残酷的解决方案,但是嘿 - 它的 Android :(

于 2012-09-19T21:21:47.400 回答
4

当我尝试通过 Intent 发送位图时遇到了同样的问题,同时我折叠了应用程序。

它在本文中的描述在此处输入链接描述它发生在 Activity 正在停止的过程中,这意味着 Activity 正试图将其保存的状态 Bundles 发送到系统操作系统以安全保存以便以后恢复(在配置更改后或进程死亡),但它发送的一个或多个 Bundle 太大。

我通过在我的活动中覆盖 onSaveInstanceState 通过 hack 解决了它:

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

并评论呼叫超级。这是一个肮脏的黑客,但它工作得很好。位图已成功发送而没有崩溃。希望这会对某人有所帮助。

于 2018-05-31T07:26:23.543 回答
4

您已经从onSaveInstanceState方法中清除了旧的 InstanceState ,它会运行良好。我正在为我的 viewpager 使用FragmentStatePagerAdapter,因此我将 Override 方法保留在我的父活动中以清除 InstanceState。

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

我从这里的android.os.TransactionTooLargeException on Nougat找到了这个解决方案

于 2018-07-07T10:36:17.917 回答
4

我们的应用程序也有这个问题。经测试发现,当应用内存不足,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;
}
于 2021-02-09T06:35:17.460 回答
3

最近我在使用 Android 的Contacts Provider时也遇到了一个有趣的案例。

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

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

我的主要错误是我没有关闭从 Contacts Provider 获取的Cursorblob 数据,因此分配给提供程序的内存增加了,这使 Binder 缓冲区膨胀,直到我!!!FAILED BINDER TRANSACTION!!!在 LogCat 输出中收到大量消息。

所以主要的想法是,当你使用外部内容提供者并Cursor从他们那里得到 s 时,当你完成使用它们时总是关闭它。

于 2013-06-03T11:39:48.390 回答
3

这可能会发生,因为 Activity "A" 可能有 Fragments 并且当您导航到 Activity "B" 时。

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

OnResume->OnPause()->OnSavedInsanceState()

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

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

}
于 2021-01-12T15:26:28.623 回答
2

就我而言,在本机库与 SIGSEGV 崩溃后,我得到 TransactionTooLargeException 作为二次崩溃。未报告本机库崩溃,因此我只收到 TransactionTooLargeException。

于 2015-08-11T21:32:54.847 回答
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));
}
于 2016-01-15T08:56:22.397 回答
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);
    }
}
于 2017-09-28T10:56:52.037 回答
2

这发生在我的应用程序中,因为我在片段的参数中传递了一个搜索结果列表,将该列表分配给片段的属性 - 这实际上是对片段参数指向的内存中相同位置的引用 - 然后添加列表中的新项目,这也改变了片段参数的大小。当活动暂停时,基片段类尝试将片段的参数保存在 onSaveInstanceState 中,如果参数大于 1MB,则会崩溃。例如:

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的帮助,我可能永远不会找到这个。

于 2019-01-04T19:09:24.610 回答
1

确保不要将大尺寸的 Intent 对象数据放入其中。在我的情况下,我添加了 String 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);
于 2013-01-14T22:40:13.693 回答
1

当我WebView在我的应用程序中处理它时,它会发生。我认为它addView与 UI 资源有关。在我的应用程序中,我在WebViewActivity下面添加了一些代码,然后它运行正常:

@Override
protected void onDestroy() {
    if (mWebView != null) {
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);  
        mWebView.removeAllViews();  
        mWebView.destroy();
    }
    super.onDestroy();
}
于 2014-12-09T06:10:16.543 回答
1

我找到了这个问题的根本原因(正如 mvds 所说,我们同时遇到了“添加窗口失败”和文件描述符泄漏)。

Android 4.4中存在一个错误。BitmapFactory.decodeFileDescriptor()它仅在inPurgeableinInputShareableofBitmapOptions设置为时发生true。这会在许多地方与文件交互导致许多问题。

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

Universal Image Loader受此问题影响。PicassoGlide似乎没有受到影响。 https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020

于 2015-06-17T10:37:59.607 回答
1

writeToParcel(Parcel dest, int flags) 方法中的这一行代码帮助我摆脱了 TransactionTooLargeException。

dest=Parcel.obtain(); 

仅在此代码之后,我将所有数据写入包裹对象,即 dest.writeInt() 等。

于 2018-04-16T13:34:21.910 回答
1

尝试使用EventBusContentProvider喜欢的解决方案。

如果您在同一个进程中(通常您的所有活动都将是),请尝试使用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 */ }

如果 Intent 的两侧不在同一个进程中,请尝试一下ContentProvider


请参阅TransactionTooLargeException

Binder 事务因太大而失败。

在远程过程调用期间,调用的参数和返回值作为存储在 Binder 事务缓冲区中的 Parcel 对象进行传输。如果参数或返回值太大而无法放入事务缓冲区,则调用将失败并抛出 TransactionTooLargeException。

于 2018-05-07T13:49:54.500 回答
1

我从 Android Espresso 测试中的 Stackoverflow 错误中得到了 TransactionTooLargeException。当我为我的应用程序移除 Logcat 过滤器时,我在日志中发现了 stackoverflow 错误堆栈跟踪。

我猜测 Espresso 在尝试处理非常大的异常堆栈跟踪时会导致 TransactionTooLargeException。

于 2018-07-02T09:49:28.890 回答
1

我也面临从一个活动传递到另一个活动的位图数据的这个问题,但是我通过将我的数据作为静态数据来解决这个问题,这对我来说是完美的

首先在活动中:

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;
}
于 2018-09-04T21:26:18.687 回答
1

@Vaiden 的回答帮助了我。我不明白为什么短名单会引发这种例外。我有很多片段和几个ViewPager带有列表的 s。所以,每次我开始另一个活动或停止一个程序(关闭屏幕)时,我都会捕捉到这个异常(通常在小米上)。

我发现所有片段都调用了它们的onSaveInstanceState()事件,并最终托管了调用onSaveInstanceState()before的活动onStop()。之后发生了崩溃。因此,我了解到许多短列表(大小为 10-100 Kb)可能会引发TransactionTooLargeException异常。

您可以通过将数据写入数据库然后通过它们id的 s 获取项目来克服这个问题。这样,您可以将ids 列表传递给 Activity/Fragment。

但是,如果您想要一种临时的快速方法,请执行此操作。

1)创建一个保留实例片段,它将在活动重新创建期间存活。

class SavedInstanceFragment : Fragment() {

    // This map will hold bundles from different sources (tag -> bundle).
    private lateinit var bundleMap: HashMap<String, Bundle?>


    // This method is only called once for this fragment.
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        retainInstance = true
        bundleMap = HashMap()
    }

    fun pushData(key: String, bundle: Bundle): SavedInstanceFragment {
        if (bundleMap[key] == null) {
            bundleMap[key] = bundle
        } else {
            bundleMap[key]!!.putAll(bundle)
        }
        return this
    }

    fun popData(key: String): Bundle? {
        val data = bundleMap[key]
        bundleMap[key] = null
        return data
    }

    fun removeData(key: String) {
        bundleMap.remove(key)
    }


    companion object {

        private val TAG = SavedInstanceFragment::class.java.simpleName

        // Create the fragment with this method in `onCreate()` of an activity.
        // Then you can get this fragment with this method again.
        fun getInstance(fragmentManager: FragmentManager): SavedInstanceFragment {
            var out = fragmentManager.findFragmentByTag(TAG) as SavedInstanceFragment?

            if (out == null) {
                out = SavedInstanceFragment()
                fragmentManager.beginTransaction().add(out, TAG).commit()
            }
            return out
        }
    }
}

2) 在onCreate()您的包含问题片段的活动中,创建此片段。它将在活动娱乐中幸存下来。您应该在将其他片段添加到 之前创建它FragmentManager,因为此操作是异步的。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity)

    if (savedInstanceState == null) {
        SavedInstanceFragment.getInstance(supportFragmentManager)
    }

    ...
}

3)在每个问题片段中添加这些行。您应该随处访问一个保留实例片段getInstance(activity!!.supportFragmentManager),以便您可以在内部找到它FragmentManager。如果childFragmentManager用作 的参数getInstance(),那么您将创建另一个片段并崩溃。

arguments在您阅读它们并设置 abundle之后,例如在onSaveInstanceState:中设置 a也很清楚arguments?.clear()。这非常重要,因为当您创建新片段时,参数会保留在内存中。当您稍后返回当前片段并旋转屏幕时,数据不会丢失,因为它们已经包含在 a 中bundle并将被读取到onCreate.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val bundle = if (savedInstanceState == null) {
        // Remove old data in order not to show wrong information.
        SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).removeData(TAG)
        null
    } else {
        SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).popData(TAG)
    }
    (bundle ?: arguments)?.run { // I access 'bundle' or 'arguments', depending if it is not first or first call of 'onCreate()'.
        token = getString(ARG_TOKEN)!!
        id = getInt(ARG_ID)
        items = getParcelableArrayList(ARG_ITEMS)
    }
}

override fun onSaveInstanceState(outState: Bundle) {
    // Create a copy of savedInstanceState and push to the retain-instance fragment.
    val bundle = (outState.clone() as Bundle).apply {
        putString(ARG_TOKEN, token)
        putInt(ARG_ID, id)
        putParcelableArrayList(ARG_ITEMS, items)
    }
    SavedInstanceFragment.getInstance(activity!!.supportFragmentManager).pushData(TAG, bundle)
    arguments?.clear() // Avoids an exception "java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size xxxxxxx bytes".
    super.onSaveInstanceState(outState)
}


companion object {

    private val TAG = YourFragment::class.java.simpleName

    private const val ARG_TOKEN = "ARG_TOKEN"
    private const val ARG_ID = "ARG_ID"
    private const val ARG_ITEMS = "ARG_ITEMS"

    fun newInstance(token: String, id: Int, items: ArrayList<Item>?) =
        YourFragment().apply {
            arguments = Bundle().apply {
                putString(ARG_TOKEN, token)
                putInt(ARG_ID, id)
                putParcelableArrayList(ARG_ITEMS, items)
            }
        }
}

您可以使用Singleton代替持久片段。另请阅读https://medium.com/@mdmasudparvez/android-os-transactiontoolargeexception-on-nougat-solved-3b6e30597345了解有关、解决方案的更多信息。

我想,如果您的活动在开始其他活动后被删除,则此解决方案将不起作用。您可以在打开Do not keep activities开发人员选项中的复选框时检查此行为(不要忘记取消选中之后)。在这种情况下getFragmentManager()将是新的,您将不会获得旧数据。当您从新活动返回时,您将获得null一个bundle.

于 2019-01-30T13:50:31.820 回答
1

我也经历过 TransactionTooLargeException。首先,我一直在努力了解它发生在哪里。我知道它发生的原因。我们每个人都知道,因为内容很大。我的问题就是这样,我解决了。也许这个解决方案对任何人都有用。我有一个从 api 获取内容的应用程序。我在第一个屏幕中从 API 获取结果并将其发送到第二个屏幕。我可以成功将此内容发送到第二个屏幕。如果我想进入第三个屏幕,则在第二个屏幕之后会发生此异常。我的每个屏幕都是从 Fragment 创建的。我注意到当我从第二个屏幕离开时。它保存其捆绑内容。如果此内容太大,则会发生此异常。我的解决方案是在我从捆绑包中获取内容后清除它。

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }
于 2019-04-18T11:56:48.763 回答
1

有时Activity包括一些需要完全重新创建片段内容的情况,如果没有明确的Fragment历史片段ActivitysportFragmentManager.fragments

    val fragments = sportFragmentManager.fragments
    val transaction = sportFragmentManager.beginTransaction()
    for (frag in fragments){
        transaction.remove(frag)
    }
    transaction.commitAllowingStateLoss()

几次重新创建片段Activity会发生(调试使用tooLargeTool

 W/ActivityStopInfo: Bundle stats:
 W/ActivityStopInfo:   android:viewHierarchyState [size=2304]
 W/ActivityStopInfo:     android:views [size=2256]
 W/ActivityStopInfo:   android:support:fragments [size=519072]
 W/ActivityStopInfo: PersistableBundle stats:
 W/ActivityStopInfo:   [null]
于 2019-07-15T10:02:03.733 回答
1

问题通过以下方式解决:

 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();
    }
}
}
于 2020-08-21T12:52:48.590 回答
0

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

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

有关更多信息,请参阅我对此相关问题的回答

于 2016-11-06T21:34:00.803 回答
0

作为 Intents、Content Providers、Messenger,所有系统服务(如电话、振动器等)都使用 Binder 提供的 IPC 基础设施。此外,活动生命周期回调也使用此基础设施。

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

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

于 2017-06-12T06:58:44.413 回答
0

有这么多地方会发生TransactionTooLargeException - 这是 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 有很多内容时,似乎会导致 TLE。

一些事情可能会减轻它(或者像我测试的那样):添加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 子类本身中的错误。但我认为这真的不明智...

于 2018-01-03T00:59:42.793 回答
0

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

例如,如果您想将大型位图图像从活动A发送到活动B,则将该位图图像存储在全局变量中

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

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

Bitmap bitmap = ((Global) this.getApplication()).getBitmap();
于 2018-01-24T12:17:27.190 回答
0

一个可以使用:

android:largeHeap="true"

在应用程序标签下的 Android 清单中。

这解决了我的问题!

于 2018-08-24T06:08:58.980 回答
0

对我来说,这个错误出现在演示者中。我对 onResume 发表了评论并在 onStart 中编写了相同的代码,它对我有用。

 @Override
    public void onStart() {
        super.onStart();
        Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
        if (goal != null && goal.getValue() > 0) {
            mCurrentValue = (int) goal.getValue();
            notifyPropertyChanged(BR.currentValue);
            mIsButtonEnabled.set(true);
        }
    }
   /* @Override
    public void onResume() {
        super.onResume();
        Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
        if (goal != null && goal.getValue() > 0) {
            mCurrentValue = (int) goal.getValue();
            notifyPropertyChanged(BR.currentValue);
            mIsButtonEnabled.set(true);
        }
    }*/
于 2018-12-06T10:33:55.647 回答
0

如果您在项目中将位图转换为 Base64 并将其保存到可打包对象中,您应该使用以下代码调整位图大小,

用 jpeg 替换 png 并将质量 100 替换为 75 或 60 :

bitmap.compress(Bitmap.CompressFormat.JPEG, 75, byteArrayOutputStream)

这个解决方案对我有用

于 2019-06-17T09:27:50.840 回答
0

在我的情况下,TransactionTooLargeException 的原因是我将大数据发送到参数中的片段(使用 Bundle),如下所示:

        var arguments = Bundle()
        arguments.putSerializable("argumentName", argumentValue)
        fragment.arguments = arguments

只有当argumentValue 具有小尺寸(例如Int 或String)时它才能正常工作,但如果它具有大尺寸(例如DTO 列表) - 您可以获得TransactionTooLargeException。所以现在我将参数传递给构造函数中的片段,一切正常。

PS 感谢sulaiTooLargeTool

于 2020-07-30T10:08:59.893 回答
0

我在 ViewPager2 和 FragmentStateAdapter 上遇到了同样的问题,这里的问题是将 FragmentStateAdapter 标记为“saveState()”作为最终的,而 ViewPager2 类也是最终的,所以我们不能覆盖其他答案中建议的方法,从而使现有答案不适用。

在我的情况下,通过在视图寻呼机的 XML 条目中添加 \android:saveEnabled="false" ' 来不保存寻呼机的状态,我能够摆脱事务太大的问题

<androidx.viewpager2.widget.ViewPager2
    android:saveEnabled="false"
    android:id="@+id/viewPager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
于 2021-03-15T13:55:09.593 回答
-1

如果您将 ViewPager2 FragmentStateAdapter 与导航组件一起使用:

我在 onViewCreated() 中创建了适配器,但每次导航返回时都会重新创建视图。由于某种原因,旧适配器没有正确分离并增加捆绑包大小,直到发生错误。我使用 TooLargeTool 对其进行调试,解决方案是避免在 onViewCreated() 中重新创建适配器。

在我的片段中,我有适配器变量:

var pagerAdapter:HomePagerAdapter?=null

在 onViewCreated 方法中,我只创建了一次适配器:

if(pagerAdapter == null){
  pagerAdapter = HomePagerAdapter(childFragmentManager, lifecycle, myContent)
}

为了防止 IllegalArgumentException,我手动将适配器从 onDestroyView 内的寻呼机中分离:

override fun onDestroyView() {
   pager.adapter = null
   super.onDestroyView()
}
于 2021-05-27T19:42:51.430 回答
-6

另一个可能的原因:

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

使用此代码查看其他手机上会发生什么会很有趣:

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

我并不是说您应该使用该代码。

于 2012-09-25T10:22:37.073 回答