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

The Binder transaction failed because it was too large. During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown. ... There are two possible outcomes when a remote procedure call throws TransactionTooLargeException. Either the client was unable to send its request to the service (most likely if the arguments were too large to fit in the transaction buffer), or the service was unable to send its response back to the client (most likely if the return value was too large to fit in the transaction buffer). ...

在某个地方,我传递或接收的参数超出了未知的限制。在哪里?

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


当前回答

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

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

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

其他回答

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

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

您可以通过将数据写入数据库,然后通过它们的id获取项来解决这个问题。通过这种方式,你可以将一个id列表传递给一个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()的参数,那么您将创建另一个片段并崩溃。

也可以在读取参数并设置包后清除参数,例如,在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)
            }
        }
}

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

我认为,如果在启动其他活动后删除了活动,则此解决方案将不起作用。您可以在打开“不将活动保留在开发人员选项中”复选框时勾选此行为(不要忘记在之后取消勾选)。在这种情况下,getFragmentManager()将是新的,您将不会得到旧的数据。当您从new activity返回时,您将在一个bundle中获得null。

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

则活动“A”的活动生命周期为

OnResume - > OnPause () > OnSavedInsanceState ()

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

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

}

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

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

bitmap.compress(bitmap.compress格式.JPEG, 75, bytearyoutstream)

这个解决办法对我很管用

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

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

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

这不是一个确定的答案,但它可能有助于阐明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的源代码会发现这些行:

企业/ / core / jni / cpp android_util_Binder。依据:

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系统中,打开文件/套接字失败并不是很干净的处理/报告。