在TransactionTooLargeException上做什么


239

我有一个TransactionTooLargeException。不可复制。在文档中说

活页夹交易失败,因为它太大。

在远程过程调用期间,调用的参数和返回值将作为存储在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)

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

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


不。但是我没有再得到它。在实时应用中安装错误跟踪器,大约3周内只有一次。至少它似乎不经常发生。也许值得在Android上打开一个问题,尽管...
Ixx 2012年

我没有答案,但这确实导致我的Galaxy S2硬重置。
Timmmm 2012年

我刚刚在我的一个应用程序中发生了这种情况。对于Galaxy S3,这种情况也只发生过一次。有趣的是,这似乎只是在使用功能更强大的设备时才有所了解。
danwms 2012年

该异常已添加到API 15中,developer.android.com / reference / android / os /…而且我在滚动地图时在MapView中重现了该异常。直到gc写下我没有记忆了。(花了我几分钟的时间)
meh 2012年

在所有设备上,事务缓冲区的大小限制为1MB,该缓冲区将使每个事务都旧。因此,设备功能越强大,它可以同时执行更多的事务,所有事务都消耗相同的1MB缓冲区。也就是说,您的答案不是答案,而是评论。
13年

Answers:


158

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

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

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

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

收到此异常时如何处理

如果可能的话,将大操作分成几个小块,例如,不要用1000个操作调用applyBatch(),而要分别使用100个操作。

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

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


1
假设我正在检查我的.apk是否已安装?在安装时...我在检查我的包com.test.installedornot..apk的大小超过9MB时遇到了相同的异常,那么在这种情况下,我将如何处理此异常?
DJhon 2014年

15
我刚在致电时收到此异常getInstalledApplications。如何解决这个问题?
斯坦(Stan)

1
@Stan此api很常见,并且广泛用于android app。当我使用此api时,此异常确实使我感到担忧。
Peacepassion 2014年

7
我可以证实您的结论,关于限制大约在500KB左右,但这是特定于设备的,在某些设备上您几乎可以传输整个1MB。我也有这个例外,所以我做了一些调查,写了一篇帖子,对于有这个问题的人来说可能很有趣。nemanjakovacevic.net/blog/english/2015/03/24/…–
Nemanja Kovacevic

11
如果您发现很难准确找出导致崩溃的状态,那么您可能会发现TooLargeTool很有用。
Max Spencer

48

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

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


9
最被低估的解决方案。此工具可帮助您缩小有问题的活动
Kedar Paranjape

这个工具可以解决我的问题。容易安装,也容易使用。
卡洛斯(Carlos)

这个工具是针对kotlin的:
maxwellnewage

2
@maxwellnewage:似乎最新版本(0.2.1、0.2.1、0.2.1)目前在仅Java应用程序中也不起作用。我必须使用0.1.6版,然后效果很好
heisenberg

使用该工具,我发现我在片段中使用了巨大的捆绑包。我所做的是从捆绑软件中提取参数,并使用bundle.clear()
EJ Chathuranga

41

这不是一个确定的答案,但可以阐明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的源代码可以发现以下几行:

frameworks / base / core / 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);

对我来说,听起来我可能正在使用此未记录的功能,该事务失败的原因不是事务太过庞大。他们应该给它起名字TransactionTooLargeOrAnotherReasonException

目前,我还没有解决问题,但是如果我发现有用的东西,我将更新此答案。

更新:原来我的代码泄漏了一些文件描述符,在Linux中它的数量已达到最大值(通常为1024),这似乎触发了异常。因此,毕竟这是一个资源问题。我通过打开/dev/zero1024次来验证这一点,这导致UI相关操作中发生各种奇怪的异常,包括上述异常,甚至是某些SIGSEGV。显然,无法打开文件/套接字并不是在整个Android系统中都能非常干净地处理/报告的问题。


36

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

发生了什么事是我们使用FragmentStatePagerAdapterViewPager。用户将翻阅并创建100多个片段(其阅读应用程序)。

尽管我们在中正确管理了片段destroyItem(),但是在Android的实现中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和OS不希望它超过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;
}

对我来说最好的答案。非常感谢
pavel

在所有可能的方法中,对我而言,这似乎是唯一的选择。在什么状态下存储的数据将使其达到此大小?如果非常需要状态,以至于有代码可以保存它,这又不会引起其他问题吗?谢谢
肯尼(Kenny)

与@Kenny相同的问题
兔子先生

1
@Override public Parcelable saveState() { Bundle bundle = (Bundle) super.saveState(); if (bundle != null) { Parcelable[] states = bundle.getParcelableArray("states"); // Subset only last 3 states if (states != null) states = Arrays.copyOfRange(states, states.length > 3 ? states.length - 3 : 0, states.length - 1); bundle.putParcelableArray("states", states); } else bundle = new Bundle(); return bundle; }
拉米·萨布里

我只让最后3个状态检查上面的代码。
拉米·萨布里

20

对于那些对寻找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保持在实例状态,您可以使用该ID来检索此数据。


是否可以将其保留为全局参数并在以后使用?
Jitendra ramoliya

@Jitendraramoliya是的,可以。这就是我的意思。
Yazon2006年

13

通常在将应用程序发送到后台时引发此异常。

因此,我决定使用数据片段方法来完全规避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/


1
如果应用程序在后台运行并且您尝试前台运行时破坏了活动,会发生什么情况?
主灾难

在这种情况下,@ MasterDisaster不会保存任何状态,因为保存实例片段的进程已经死亡。
Vaiden

权限,因此这种情况仅适用于配置更改。
Master Disaster

只要操作系统触发onSavedState(),它就可以工作,这在许多情况下都会发生。配置更改是其中之一。切换应用程序并进入后台是另一回事。还有更多。
Vaiden

1
我认为应该扩展此解决方案以保存来自不同来源的数据。可能是一个HashMap,其标签为键,捆绑为值
。。– CoolMind

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

代替这个:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);

9

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

我相信它还会保存一些特定的对象,例如包裹等(Parcel.obtain()),因此始终将每个obtain()与匹配非常重要recycle()

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

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

但是,该调用仍可能失败,因此将其括在a中catch并在必要时能够重试很重要。

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


感谢您提供信息,该缓冲区在应用程序内的所有事务之间共享!
Artem Mostyaev 2014年

1
如果是这种情况,那为什么会这样呢?我只能使用Android 4.4手机,而没有其他地方。我认为它更多是4.4中的错误,我无法弄清原因。
JPM

9

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

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

使用DDMS并在播放应用程序时查看您的堆,这将给您一些指示,指出哪个setcontentview会产生问题。

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

问题已解决。


内存/位图异常通常看起来有所不同。我已经看到很多人使用Android 2.x-4.x进行测试,并且异常看起来总是不同的。但是,谁知道呢,也许这也与4.x版本相关,但特定于4.x版本。
Ixx 2012年

10
关于信息,这只是一个可怕的例外,因为它无法提供有关问题出处的任何线索。
Ixx 2012年

我认为几天过去了,您的发现是什么?
丹尼

8

将此添加到您的活动

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

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


7
我认为,这是最有害的提示。为什么我们要清除要还原的数据onCreate()
CoolMind

2
此代码的含义是您最终不会保存实例状态...
贾斯汀

4

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


4

您已经从onSaveInstanceState 方法中清除了旧的InstanceState ,它将很好地工作。我正在为我的viewpager 使用FragmentStatePagerAdapter,所以我将Override方法保持在父活动下以获取清晰的InstanceState。

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

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


非常感谢。
Andrain

谢谢您的回答,请帮帮我
Jatin Patel

3

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

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

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

我的主要错误是,我没有关闭Cursor与联系人提供商得到BLOB数据,以使分配给该供应商的内存增加,这种膨胀的粘结剂缓冲,直到我得到吨的!!!FAILED BINDER TRANSACTION!!!在我的logcat输出消息。

因此,主要思想是,当您与外部内容提供商合作并Cursor从他们那里获得服务时,请在与他们合作时始终关闭它。


3

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

本文中描述的方式在此处输入链接描述,当活动处于停止状态时会发生这种情况,这意味着该活动正试图将其保存的状态捆绑发送到系统OS以安全保存,以便以后进行恢复(在配置更改后)或进程死亡),但它发送的一个或多个捆绑包太大。

我通过重写Activity中的onSaveInstanceState来解决它:

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

和评论通话超级。这是一个肮脏的hack,但是它运行良好。位图已成功发送,没有崩溃。希望这会帮助某人。


2

就我而言,在本机库因SIGSEGV崩溃之后,我将TransactionTooLargeException作为第二次崩溃。未报告本机库崩溃,因此我仅收到TransactionTooLargeException。


2

尝试批量插入较大的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));
}

2
如果另一个失败了怎么办?您需要使用循环进行更多划分。有没有办法获取交易规模并获得最大交易规模?
Android开发者

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

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

这是个坏主意。以这种方式使用静态变量是代码的味道。另外,尝试使用“不要保留活动”标志运行此代码,并在导航到PageWebView之后按Home键。再次打开活动后,您可能会崩溃,因为MainActivity的所有变量都会失效100%。
Kyrylo Zapylaiev

1

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

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

1

我发现了这个问题的根本原因(如mvds所说,“添加窗口失败”和文件描述符泄漏)。

有一个错误BitmapFactory.decodeFileDescriptor()Android 4.4系统中。仅当inPurgeableinInputShareableBitmapOptions设置为时才会发生true。这在许多地方导致与文件交互的许多问题。

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

Universal Image Loader受到此问题的影响。毕加索格莱德似乎没有受到影响。 https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

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

dest=Parcel.obtain(); 

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


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事务缓冲区中的包裹对象进行传输。如果参数或返回值太大而无法放入事务缓冲区中,则调用将失败,并且将引发TransactionTooLargeException。


1

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

我猜想Espresso在尝试处理非常大的异常stacktrace时会导致TransactionTooLargeException。


1

一个可以使用:

android:largeHeap="true"

在Android Manifest中的application标记下。

这解决了我的问题!


就我而言(由于调用onSaveInstantState活动/片段并保存大量列表)没有帮助。
CoolMind

1
这被认为是不好的做法,因为您没有处理为什么首先要保存大量数据的原因。在较新的设备上,它使应用程序崩溃,并且限制小得多(256KB)。研究为什么要先存储很多并减少它。
蒂姆·基斯特

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

1

这是在我的应用程序中发生的,因为我正在传递片段参数中的搜索结果列表,并将该列表分配给片段的属性-实际上是对片段参数所指向的内存中同一位置的引用-然后添加列表中的新项目,这也更改了片段参数的大小。当活动被暂停时,基本片段类尝试将片段的参数保存在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的帮助,我可能永远找不到


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

尽管这是正确的(对我来说是可耻的,一年后我也明白这一点),但是如果片段重新创建(屏幕旋转),该怎么办?
CoolMind

0

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

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

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


0

由于Intent,内容提供者,Messenger,电话,振动器等所有系统服务都使用了Binder的IPC基础设施提供者,而且活动生命周期回调也使用了该基础设施。

1MB是特定时刻在系统中执行的所有活页夹交易的总限制。

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


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包含大量内容时,似乎会引起TTLE。

有一些事情可以减轻它的影响(或者无论如何我都在做测试):添加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子类本身中的错误。但我认为这真的不明智...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.