莫名其妙的TransactionTooLargeException

  • ~5.05K 字

错误

最近将某个老项目升级到AndroidX,并且在将minSdk由19提升至24,targetSdk由23提升至28后,在某个数据量较大的场景出现了上述错误。

原因

该问题出现在有一个界面跳转到另一个界面时,大概含义就是传递的数据量过大,超过了限制。分析抛出的原因或条件,查看源码android_util_binder.cpp,查看系统层在什么情况下会抛出TransactionTooLargeException可知在binder驱动处理失败后,如果传输的parcel体积超过200kb,则会抛出TransactionTooLargeException,因此引发该问题的原因是binder调用传输的数据太大导致,问题分析重点应侧重binder数据传输。

解决

  1. 查看代码后发现,项目中在跳转时并未携带过多数据
1
2
3
4
5
6
Intent intent = new Intent(context, TestDetectionEditTabInnerActivity.class);
intent.putExtra(NavigationViewDataInitHelper.TYPE_CODE, editViewModel.getFieldType());
intent.putExtra(TestDetectionEditTabInnerActivity.RECORD_VALUE, editViewModel.getValue());
intent.putExtra(TestDetectionEditTabInnerActivity.PROJECT_NAME, editViewModel.getName());
intent.putExtra(NavigationViewDataInitHelper.FUNCTION_IDENTIFICATION, functionIdentification);
fragment.startActivityForResult(intent, Constants.REQUEST_CODE);

其中

1
editViewModel.getFieldType()
1
editViewModel.getValue()
1
editViewModel.getName()

以及

1
functionIdentification

都是String类型的数据,并且数据长度都不大。 综合实际排错过程中的操作,只有在一个内嵌的fragment数据加载时才会出现上述错误,那我们先去fragment中看下:

1
2
3
4
5
6
7
8
9
10
11
@Override
public Bundle createBundle(QFormViewModel viewModel, ExpandFormSchemaModel dataModel) {
Bundle bundle = new Bundle();
bundle.putString(INTENT_SMART_INPUT_FRAGMENT_SOURCE, dataModel.getDataSource());
mParamValidators = dataModel.getParamValidators();
mDatasourceKey = dataModel.getDatasourceKey();
mJsonBody = dataModel.getJsonBody();
mListSource = dataModel.getListSource();
mFunctionIdentification = dataModel.getFunctionIdentification();
return bundle;
}

好家伙,先把这个bundle干掉再说

1
2
3
4
5
6
7
8
9
@Override
public Bundle createBundle(QFormViewModel viewModel, ExpandFormSchemaModel dataModel) {
mParamValidators = dataModel.getParamValidators();
mDatasourceKey = dataModel.getDatasourceKey();
mJsonBody = dataModel.getJsonBody();
mListSource = dataModel.getListSource();
mFunctionIdentification = dataModel.getFunctionIdentification();
return null;
}

重新编译运行,发现还是GG

  1. 那么说明还有其他的地方有大量数据被带入到跳转时候的binder中。 回到先前跳转的地方,我们发现startActivityForResult的地方居然是用的fragment,原来在这儿呢,看到上面的context直接就是一个activity引用,直接使用context.startActivityForResult,果然重新验证后没有出现数据传递超过限制的问题了,看来问题就在这个fragment中。
  2. 来到这个fragment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static TestDetectionEditTabFragment newInstance(
List<TestDetectionEditViewModel> models,
String recordId,
String functionIdentification,
String type, String projectName, String defaultValue) {
Bundle args = new Bundle();
args.putParcelableArrayList(MODELS, (ArrayList<? extends Parcelable>) models);
args.putString(RECORD_ID, recordId);
args.putString(FUNCTION_IDENTIFICATION, functionIdentification);
args.putString(TYPE, type);
args.putString(PROJECT_NAME, projectName);
args.putString(DEFAULT_VALUE, defaultValue);
TestDetectionEditTabFragment fragment = new TestDetectionEditTabFragment();
fragment.setArguments(args);
return fragment;
}

我们看到在静态方法中往Bundle中塞入了很多的数据,这些数据在先前startActivityForResult的时候就是作为引用context包含在binder中。
找到Bundle数据获取完的地方,对Bundle实施clear方法。一般是onViewCreated方法中,我这里是自己定义的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void iData(View view) {
Bundle arguments = getArguments();
if (arguments != null) {
models = arguments.getParcelableArrayList(MODELS);
recordId = arguments.getString(RECORD_ID);
functionIdentification = arguments.getString(FUNCTION_IDENTIFICATION);
type = arguments.getString(TYPE);
projectName = arguments.getString(PROJECT_NAME);
defaultValue = arguments.getString(DEFAULT_VALUE);
setAdapter(models);
arguments.clear();
}
}

  1. 改完后发现影响了其他功能

    原来项目中针对fragment专门处理了onActivityResult,导致重新进入的时候Bundle中的数据获取不到引起了其他的错误。
    最后,直接在需要传值的地方,将数据传递给成员变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static TestDetectionEditTabFragment newInstance(
List<TestDetectionEditViewModel> models,
String recordId,
String functionIdentification,
String type, String projectName, String defaultValue) {
Bundle args = new Bundle();
args.putString(RECORD_ID, recordId);
args.putString(FUNCTION_IDENTIFICATION, functionIdentification);
args.putString(TYPE, type);
args.putString(PROJECT_NAME, projectName);
args.putString(DEFAULT_VALUE, defaultValue);
TestDetectionEditTabFragment fragment = new TestDetectionEditTabFragment();
fragment.setArguments(args);
fragment.setModels(models);
return fragment;
}

通过set方法直接赋值

1
2
3
public void setModels(List<TestDetectionEditViewModel> models){
this.models = models;
}

总结

处理TransactionTooLargeException的根本还是要针对数据传输做优化,要么使用类似EventBus的方法,要么使用成员变量直接赋值,避开通过Bundle传值这个坑。

赞助喵
非常感谢您的喜欢!
赞助喵
分享这一刻
让朋友们也来瞅瞅!