在TransactionTooLargeException上做什么

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

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

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

228831 次浏览
我也在三星S3上得到了这个异常。 我怀疑有两个根本原因,

  1. 如果你有位图,它会占用太多内存,使用缩小
  2. 你在drawable-_dpi文件夹中丢失了一些drawables, android在drawable中寻找它们,并调整它们的大小,使你的setContentView突然跳转并使用大量内存。

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

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

问题解决了。

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

另一个可能的原因是:

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

看看在其他手机上用这个代码会发生什么会很有趣:

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

我不是说你应该使用那个代码。

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

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

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

  1. 您是否在服务和应用程序之间交换了大量数据?
  2. 使用intent共享海量数据,(例如用户从gallery共享中选择大量文件按share,所选文件的uri将使用intent传输)
  3. 从服务接收位图文件
  4. 等待android返回大量数据(例如,当用户安装了大量应用程序时,getInstalledApplications())
  5. 使用applyBatch()处理大量未决操作

当你得到这个异常时如何处理

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

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

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

确保你没有在Intent对象中放入大容量的数据。在我的情况下,我添加字符串500k大小,然后开始另一个活动。除了这个例外,它总是失败。我通过使用活动的静态变量避免了在活动之间共享数据——你不需要将它们发送到Intent,然后从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);

这不是一个明确的答案,但它可能会揭示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系统中,打开文件/套接字失败并不是很干净的处理/报告。

最近,我在使用Android的< em >联系供应商< / em >时也遇到了一个有趣的情况。

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

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

我的主要错误是我没有结束Cursor与blob数据从接触提供者,因此分配给提供者的内存增加,这膨胀的Binder缓冲区,直到我得到大量的!!!FAILED BINDER TRANSACTION!!!消息在我的LogCat输出。

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

重要的是要理解,无论设备功能或应用程序,事务缓冲区被限制为1mb。这个缓冲区用于你所做的每个API调用,并在应用程序当前运行的所有事务之间共享。

我相信它还包含一些特定的对象,如包裹和这样的(Parcel.obtain()),所以总是将每个obtain()recycle()匹配是很重要的。

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

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

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

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

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

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

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

android4.4的BitmapFactory.decodeFileDescriptor()中有一个错误。 只有当BitmapOptionsinPurgeableinInputShareable被设置为true时才会发生。这在与文件交互的许多地方导致了许多问题

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

通用图像加载器受此问题影响。毕加索滑翔似乎不受影响。 https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020 < / p >

在我的情况下,我得到TransactionTooLargeException作为二次崩溃后本机库崩溃与SIGSEGV。本机库崩溃没有报告,所以我只收到TransactionTooLargeException。

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

当我试图bulkInsert一个大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));
}

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

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

更多信息见我对这个相关问题的回答

对于那些在寻找为什么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的实例状态,您可以使用检索此数据。

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

发生的事情是我们在ViewPager中使用了FragmentStatePagerAdapter。用户将翻页并创建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。当该数组被转储到parcelableArray并且操作系统不喜欢它有100+项时,将发生TransactionTooLargeException

因此,我们的修复是覆盖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;
}

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

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

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

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

因此,我决定使用数据片段模式来完全绕过onSavedInstanceState生命周期。我的解决方案还处理复杂的实例状态并尽快释放内存。

首先,我创建了一个简单的片段来存储数据:

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的状态中):

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

对我来说,它也是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位置,并在被覆盖的onSaveInstanceState()方法中调用adapter.removeFragments():

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

有这么多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 >= Build.VERSION_CODES.O) {
et.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}

第二种糟糕的解决方法可能是重写performClick()onWindowFocusChanged()方法来捕获TextEdit子类本身的错误。但我不认为这是明智的……

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

例如,如果你想从活动一个向活动B发送一个大的位图图像,然后将该位图图像存储在全局变量中

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

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

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

这一行代码在writeToParcel(包裹dest, int标志)方法帮助我摆脱TransactionTooLargeException。

dest=Parcel.obtain();

在这段代码之后,我只写所有的数据到包裹对象,即dest.writeInt()等。

如果你需要调查哪个包裹导致了你的崩溃,你应该考虑尝试TooLargeTool

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

尝试使用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事务失败,因为它太大了。

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

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

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

我通过重写onSaveInstanceState在我的活动中解决了这个问题:

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

和注释调用super。这是一个肮脏的hack,但它是完美的工作。位图成功发送,没有崩溃。

.

.

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

我猜Espresso在试图处理一个非常大的异常堆栈跟踪时引起了TransactionTooLargeException。

你已经从仅有方法中清除了旧的InstanceState,它会工作得很好。我使用FragmentStatePagerAdapter为我的viewpager,所以我保持下面的Override方法到我的父活动为清晰的InstanceState。

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

我发现这个解决方案从这里android.os.TransactionTooLargeException在牛轧糖上

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

在活动中首先:

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

将此添加到您的活动中

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

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

对我来说,这个错误是在演示程序中出现的。我对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);
}
}*/

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

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

我发现所有调用他们的onSaveInstanceState()事件,最后托管活动称为onSaveInstanceState()onStop()之前。之后发生了撞车事故。因此,我理解许多短列表(10-100 Kb大小)可以引发TransactionTooLargeException异常。

你可以通过将数据写入数据库,然后通过__abc0获取项来克服这个问题。通过这种方式,你可以传递一个__abc0列表给一个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后清除它们,并设置bundle,例如,在onSaveInstanceState: arguments?.clear()中。这是非常重要的,因为当你创建一个新的片段时,参数会留在内存中。当你稍后返回到当前片段并旋转屏幕时,数据不会丢失,因为它们已经包含在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)
}
}
}

你可以使用单例代替持久片段。有关解决方案的更多信息,请参阅https://medium.com/@mdmasudparvez/android-os-transactiontoolargeexception-on-nougat-solved-3b6e30597345

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

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


lateinit var myContent: MyContent


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myContent = arguments?.getParcelable("mycontent")
arguments?.clear()
}

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

将PNG替换为jpeg,并将质量100替换为75或60:

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

这个解决办法对我很管用

有时候Activity包含一些FragmentActivity需要完全重建片段内容时, 如果sportFragmentManager.fragments没有清晰的历史片段

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

多次重新创建fragment, 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]

在我的情况下,TransactionTooLargeException的原因是我在参数中发送大数据片段(使用Bundle),像这样:

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

只有当argumentValue的大小较小(例如Int或String),它才能正常工作, 但如果它有很大的大小(例如DTO列表)-你可以得到TransactionTooLargeException。 所以现在我在构造函数中传递参数给fragment,一切正常 < p > PS 感谢sulai for TooLargeTool

问题通过以下方式解决:

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

这可能发生,因为活动"可能有片段,当你导航到活动".

然后活动生命周期的活动"将

OnResume→OnPause()→OnSavedInsanceState ()

这里的OnSavedInsanceState可能会导致崩溃,因为它不能保存太多数据的状态。因此,尝试清除活动的saveinsatce "A"通过添加以下代码。

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


}

我们的应用程序也有这个问题。经过测试,发现当应用程序内存不足,活动被回收时,系统调用仅有方法来保存大量的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;
}

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

在我的情况下,我能够摆脱交易太大的问题,不保存分页器的状态添加\android:saveEnabled="false"的XML条目

<androidx.viewpager2.widget.ViewPager2
android:saveEnabled="false"
android:id="@+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

如果你使用导航组件的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()
}

我最近也遇到了这个问题,在阅读了这个和其他网站上的所有解决方案2天后,我想分享我的问题的原因和我是如何解决的,因为我没有看到我的问题。

我使用的片段是:

  • FragmentA→包含一个位图回收视图
  • FragmentB→启动相机并将图像捕获返回片段A

FragmentB有相机的东西,所以我用previousBackStackEntry把位图发送到FragmentA,像这样:

findNavController().run {
previousBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, photo)
popBackStack()
}

最后,我在FragmentA上使用位图,这样做:

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Bitmap>(KEY_REPORT_PHOTO)
?.observe(viewLifecycleOwner) {
it?.let { photo ->
viewModel.addPhoto(photo)
findNavController().currentBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, null)
}
}

阅读本页上的信息,我的第一个方法是找出onSaveInstanceState中发生了什么,并尝试删除回收器视图中的位图,当我关闭应用程序并将其放在后台时,我认为这些位图被保存了。

最后,我意识到问题是我在导航组件的BackStackEntry中添加的位图,解决方案很容易,只要在使用FragmentA中的位图后用这一行清理它:

findNavController().currentBackStackEntry?.savedStateHandle?.set(KEY_REPORT_PHOTO, null)

我在卸载另一个应用程序正在进行IPC调用的应用程序时得到了这个错误。

一个假设是;作为“服务”;应用程序因为卸载而消失,一些传输缓冲区可能已经被填满,导致此错误,因为没有东西不再“弹出东西”。从缓冲区。

如果这是你的情况,这个问题可能没什么好担心的。