如何处理TransactionTooLargeException

时间:2012-07-12 12:01:38

标签: 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)

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

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

43 个答案:

答案 0 :(得分:143)

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

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

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

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

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

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

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

答案 1 :(得分:38)

这不是一个明确的答案,但它可能会对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的源代码,找到以下几行:

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

答案 2 :(得分:30)

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

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

虽然我们在destroyItem(),Androids中正确管理了片段 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并且操作系统不喜欢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;
}

答案 3 :(得分:28)

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

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

答案 4 :(得分:18)

对于那些因为查找TransactionTooLargeException异常的答案而感到非常失望的人,尝试检查您在实例状态中保存的内容。

在编译/ targetSdkVersion&lt; = 23时,我们只有内部警告关于大型保存状态,但没有任何内容崩溃:

if len(item) > 1:
    item[1] = item[1].title()

但是在编译/ targetSdkVersion&gt; = 24时,我们遇到了真正的RuntimeException崩溃

import csv

with open('C:\\Users\\Data.csv', 'r') as f:
    reader = csv.reader(f, delimiter='\t')
    data = list(reader)

for item in data[1:]:                          # excludes headers
    item[0] = item[0].title()                  # will capitalize the Company column too
    item[0] = item[0][0].lower() + item[0][1:] # that's why we need to revert

    print(item) 
    # see that data contains lists having one element only
    # the line above will output to
    #    ['Company        Name                 Job Title']
    #    ['xxxxxx        Jack Nicholson        Manager']
    #    ['yyyyyy        Brad Pitt             Accountant']


with open('C:\\Users\\Data.csv', 'w') as f:
    writer = csv.writer(f, delimiter='\t')
    writer.writerows(data)

怎么做?

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

答案 5 :(得分:11)

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

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

然后在我的主要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
}

剩下的只是弹出已保存的实例:

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

答案 6 :(得分:9)

这个问题没有一个具体原因。对我来说,在我的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);

答案 7 :(得分:8)

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

  1. 你有加载和占用太多内存的位图,使用缩小尺寸
  2. 你的drawable-_dpi文件夹中缺少一些drawables,android在drawable中查找它们,并调整它们的大小,使你的setContentView突然跳转并使用大量内存。
  3. 使用DDMS并在播放应用时查看您的堆,这将为您提供有关哪个setcontentview正在创建问题的一些指示。

    我复制了所有文件夹中的所有drawable,以摆脱问题2。

    问题已解决。

答案 8 :(得分:8)

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

我相信它还包含一些特定的对象,例如parcels和(Parcel.obtain()),因此始终将每个obtain()recycle()匹配非常重要。

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

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

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

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

答案 9 :(得分:7)

将此添加到您的活动

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

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

答案 10 :(得分:4)

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

答案 11 :(得分:3)

最近,我在使用Android的 通讯录提供程序 时遇到了一个有趣的案例。

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

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

我的主要错误是我没有关闭来自联系人提供程序的blob数据的Cursor,因此为提供程序分配的内存增加了,这使得Binder缓冲区膨胀直到我在LogCat输出中收到了大量!!!FAILED BINDER TRANSACTION!!!条消息。

因此,主要的想法是,当您使用外部内容提供商并从中获取Cursor时,请务必在完成使用后关闭它。

答案 12 :(得分:2)

在我的情况下,在本机库与SIGSEGV崩溃后,我将TransactionTooLargeException作为二次崩溃。未报告本机库崩溃,因此我只收到TransactionTooLargeException。

答案 13 :(得分:2)

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

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

我从这里android.os.TransactionTooLargeException on Nougat

找到了这个解决方案

答案 14 :(得分:2)

我们的应用也有这个问题。经过测试,发现当应用内存不足,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;
}

答案 15 :(得分:2)

对我而言,它也是local function X() Play("Hello.mp3") end ,但最重要的是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首次打开,或者应用程序被Android杀死后,其中Activity将创建适配器。)

onCreate

答案 16 :(得分:1)

一个可以使用:

android:largeHeap="true"

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

这解决了我的问题!

答案 17 :(得分: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;
}

答案 18 :(得分:1)

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

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

答案 19 :(得分:1)

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

本文中描述的enter link description here如何在Activity停止的过程中发生,这意味着Activity正在尝试将其保存的状态Bundle发送到系统操作系统,以便以后安全保存(在配置更改或处理死亡后)但它发送的一个或多个Bundle太大了。

我通过在我的Activity中覆盖onSaveInstanceState来解决它:

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

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

答案 20 :(得分:1)

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

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

OnResume->OnPause()->OnSavedInsanceState()

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

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

}

答案 21 :(得分: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();
    }
}
}

答案 22 :(得分:1)

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

答案 23 :(得分:1)

我找到了这个的根本原因(我们得到了&#34;添加窗口失败&#34;文件描述符泄漏,如mvds所说)。

Android 4.4的BitmapFactory.decodeFileDescriptor()中有bug。 仅当inPurgeable inInputShareable BitmapOptions设置为true时才会发生此问题。这导致许多地方的许多问题与文件交互。

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

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

答案 24 :(得分:1)

确保您没有放入大尺寸的Intent对象数据。在我的情况下,我添加了字符串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);

答案 25 :(得分:0)

有时Activity包含一些Fragment,而Activity需要完全重新创建Fragments内容, 如果sportFragmentManager.fragments没有清晰的历史记录片段

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

服务器时间重新创建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]

答案 26 :(得分:0)

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

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

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

此解决方案对我有用

答案 27 :(得分:0)

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

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

仅当argumentsValue的大小较小(例如Int或String)时,它才能正常工作, 但是如果它很大(例如DTO列表),则可以获取TransactionTooLargeException。 所以现在我将参数传递给构造函数中的fragment,一切正常。

PS 感谢sulaiTooLargeTool

答案 28 :(得分:0)

我还住过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()
    }

答案 29 :(得分:0)

如果您将 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()
}

答案 30 :(得分:0)

我在使用 ViewPager2 和 FragmentStateAdapter 时遇到了同样的问题,这里的问题是 FragmentStateAdapter 标记为“saveState()”为 final,而 ViewPager2 类也是 final,因此我们不能按照其他答案中建议的方法覆盖使现有答案不适用的方法。

在我的情况下,通过在视图寻呼机的 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" />

答案 31 :(得分:0)

@Vaiden answer帮助了我。我不明白为什么短名单会引发这种例外情况。我有很多片段,还有几个ViewPager和列表。因此,每次我开始另一项活动或停止某个程序(关闭屏幕)时,我都会捕获此异常(通常在小米上)。

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

您可以通过将数据写入数据库然后通过其id获取项目来克服此问题。这样,您可以将id的列表传递给活动/片段。

但是,如果您希望使用临时的快速方法,请执行此操作。

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
    }


    companion object {

        private const val TAG = "SavedInstanceFragment"

        // 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()的参数,那么您将创建另一个片段并崩溃。

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

    val bundle = 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)
    super.onSaveInstanceState(outState)
}


companion object {

    const val TAG = "YourFragment"

    private const val ARG_TOKEN = "token"
    private const val ARG_ID = "id"
    private const val ARG_ITEMS = "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,以获取有关解决方案的更多信息。

答案 32 :(得分:0)

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

一个更好的解决方案是不要在参数中传递太多数据。

如果没有this answerTooLargeTool的帮助,我可能永远找不到。

答案 33 :(得分: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);
        }
    }*/

答案 34 :(得分:0)

尝试使用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。

答案 35 :(得分:0)

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

dest=Parcel.obtain(); 

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

答案 36 :(得分:0)

我在向广播接收器传输(发送广播)位图对象时遇到了同样的问题。

intent.putExtra("image", bitmapImage); 

所以不要将它们作为位图发送。我将位图转换为字节数组。令我惊讶的是它有效!!!我想知道为什么Android不允许使用Bitmap传输大量数据,但允许使用相同的直通字节数组。

 intent.putExtra("imageInByteArray", convertBitmapToByteArray(bitmapImage)); 

在接收器上我将字节数组转换回位图,这解决了我的问题。

答案 37 :(得分:0)

对于我 TransactionTooLargeException ,当我尝试通过意图将大位图图像从一个活动发送到另一个活动时发生。我使用Application's Global Variables解决了这个问题。

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

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

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

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

答案 38 :(得分: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。 一些事情可能会减轻它(或者我正在测试):在EditText的xml布局声明中添加android:importantForAutofill =“noExcludeDescendants”。或者在代码中: EditText et = myView.findViewById(R.id.scriptEditTextView); if(Build.VERSION.SDK_INT&gt; = Build.VERSION_CODES.O){     et.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS); } 第二个可怕的,可怕的解决方法也可能是覆盖performClick()和onWindowFocusChanged()方法来捕获TextEdit子类本身的错误。但我认为这不是明智的......

答案 39 :(得分:0)

作为意图,内容提供商,Messenger,所有系统服务,如电话,振动器等,都使用Binder的IPC基础设施提供商。此外,活动生命周期回调也使用此基础设施。

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

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

答案 40 :(得分:0)

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

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

有关详细信息,请参阅my answer to this related problem

答案 41 :(得分:0)

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

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

答案 42 :(得分:-3)

另一个可能的原因:

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

使用此代码查看其他手机上发生的情况会很有趣:

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