Caused by: android.os.TransactionTooLargeException

 

最近在友盟后台上看到一条错误信息:

java.lang.RuntimeException: Adding window failed
	at android.view.ViewRootImpl.setView(ViewRootImpl.java:703)
	at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278)
	at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2975)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2345)
	at android.app.ActivityThread.access$800(ActivityThread.java:157)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1247)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:157)
	at android.app.ActivityThread.main(ActivityThread.java:5293)
	at java.lang.reflect.Method.invokeNative(Method.java)
	at java.lang.reflect.Method.invoke(Method.java:515)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
	at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: android.os.TransactionTooLargeException
	at android.os.BinderProxy.transact(BinderProxy.java)
	at android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:710)
	at android.view.ViewRootImpl.setView(ViewRootImpl.java:692)
	... 14 more
android.os.TransactionTooLargeException
	at android.os.BinderProxy.transact(BinderProxy.java)
	at android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:710)
	at android.view.ViewRootImpl.setView(ViewRootImpl.java:692)
	at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278)
	at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2975)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2345)
	at android.app.ActivityThread.access$800(ActivityThread.java:157)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1247)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:157)
	at android.app.ActivityThread.main(ActivityThread.java:5293)
	at java.lang.reflect.Method.invokeNative(Method.java)
	at java.lang.reflect.Method.invoke(Method.java:515)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
	at dalvik.system.NativeStart.main(NativeStart.java)

这个错误是发生在用户的一台三星手机上,设备信息如下:

Caused by: android.os.TransactionTooLargeException_第1张图片

于是针对这个错误我们来查阅一下官方文档:TransactionTooLargeException

大概意思是说,Binder事务失败,因为它太大了。

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

Binder事务缓冲区具有有限的固定大小,当前为1Mb,由进程正在进行的所有事务共享。因此,即使大多数单个事务的大小适中,当有许多事务正在进行时,也会抛出此异常。

远程过程调用抛出时有两种可能的结果 TransactionTooLargeException。客户端无法将其请求发送到服务(很可能,如果参数太大而无法容纳在事务缓冲区中),或者服务无法将其响应发送回客户端(最有可能的话,如果返回值为太大而不适合事务缓冲区)。无法确定实际发生了哪些结果。客户应该假设发生了部分故障。

避免的关键TransactionTooLargeException是保持所有交易相对较小。尝试最小Parcel化为参数创建所需的内存量以及远程过程调用的返回值。避免传输大量字符串或大位图。如果可能的话,尝试将大量请求分解成更小的部分。

如果要实现服务,则可能有助于对客户端可以执行的查询施加大小或复杂性约束。例如,如果结果集可能变大,则不允许客户端一次请求多个记录。或者,不是一次性返回所有可用数据,而是首先返回基本信息,然后根据需要让客户端请求其他信息。

 

既然原因我们已经知道了,那问题来了,我们要怎么重现这个问题呢?

案例一:intent传递集合(bug重现)

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ArrayList mList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_jump).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, TestActivity.class);
                intent.putStringArrayListExtra(TestActivity.KEY_LIST_DATA, mList);
                startActivity(intent);
            }
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100000; i++) {
                    String value = "添加元素" + i;
                    mList.add(value);
                    Log.d(MainActivity.class.getSimpleName(), value);
                }
            }
        }).start();
    }

}
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.util.ArrayList;

public class TestActivity extends AppCompatActivity {

    public final static String KEY_LIST_DATA = "list_data";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        ArrayList list = getIntent().getStringArrayListExtra(KEY_LIST_DATA);
        if (list != null) {
            Log.d(TestActivity.class.getSimpleName(), "集合大小: " + list.size());
        }
    }

}

Caused by: android.os.TransactionTooLargeException_第2张图片

不难发现,我们可以看到错误信息:Caused by: android.os.TransactionTooLargeException: data parcel size 2796360 bytes,大概意思是传递数据不能超过2796360字节(约2.6M)

 

案例二:Dialog设置标题(bug重现)

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private StringBuffer buffer = new StringBuffer();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Dialog mDialog = new Dialog(MainActivity.this);
                LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View view = inflater.inflate(R.layout.dialog_test, null);
                mDialog.setContentView(view);
                mDialog.setTitle(buffer);
                mDialog.show();
            }
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100000; i++) {
                    String value = "添加元素" + i;
                    buffer.append(value);
                    Log.d(MainActivity.class.getSimpleName(), value);
                }
            }
        }).start();
    }

}

 Caused by: android.os.TransactionTooLargeException_第3张图片

不难发现,我们可以看到错误信息:Caused by: android.os.TransactionTooLargeException: data parcel size 3555936 bytes,大概意思是传递数据不能超过3555936 字节(约3.4M)

 

解决方案:尽量避免传递过大的数据,也要避免多个线程同时调用跨进程方法

你可能感兴趣的:(android解决问题)