最近在某些项目上出现了 TransactionTooLargeException,查看崩溃日志后返现并没有给出确切的错误定位。
# 产生原因
TransactionTooLargeException 原因分析:在应用层与各种 SystemService 交互过程中,调用参数和返回值会通过 Parcel 对象存储在 Binder Transaction 缓冲区中以进行传输,如果参数或返回值太大超过了 Binder`` Transaction 缓冲区的限制大小,那么调用将会失败抛出 TransactionTooLargeException 。在日常开发中, TransactionTooLargeException 通常是由于都是 传输的数据过大 或 页面被杀死时缓存的数据过大 导致的。
# 定位问题
我们使用 toolargetool 来定位问题
dependencies { | |
... | |
implementation 'com.gu.android:toolargetool:0.3.0' | |
} |
在 Application.onCreate 方法中初始化
TooLargeTool.startLogging(this); |
使用 adb 命令打开专用监听窗口
adb logcat -s TooLargeTool |

出现组件正在向事务写入大量数据和缓冲区的时候就会出现日志
# 常见的原因
Fragment.setArguments 传输的 Bundle 过大
我们应特别注意通过
Fragment.setArguments所传递的 Bundle 的数据大小,如果数据量较大,应该选择使用其他方式进行传输。public class MemoryBigData<T> extends ZeroBaseDto{
private static final ConcurrentHashMap<String, Object> CACHE_MAP = new ConcurrentHashMap<>();
public static <T> MemoryBigData<T> of(T value) {
return of(UUID.randomUUID().toString(), value);
}/*** 通过 tag 存储 MemoryBigData
* @param tag
* @param value
* @return
* @param <T>
*/
public static <T> MemoryBigData<T> of(String tag, T value) {
MemoryBigData<T> bigData = new MemoryBigData<>(tag);
bigData.set(value);
return bigData;
}private final String tag;
/*** 通过 tag 构造 MemoryBigData
* @param tag
*/
private MemoryBigData(String tag) {
this.tag = tag;
}public void set(T t) {
CACHE_MAP.put(tag, t);
}/*** 通过 tag 获取 MemoryBigData
* @return
*/
public T get() {
return (T) CACHE_MAP.get(tag);
}/*** 通过 tag 获取 MemoryBigData 并删除
* @return
*/
public T getAndRemove() {
return (T) CACHE_MAP.remove(tag);
}/*** 判断是否存在 tag 对应的数据
* @return
*/
public boolean isPresent() {
return CACHE_MAP.containsKey(tag);
}}存储:
Bundle args = new Bundle();
args.putSerializable(MODELS,MemoryBigData.of(models));
获取:
MemoryBigData<List<TestDetectionEditViewModel>> bigData = (MemoryBigData) arguments.getSerializable(MODELS);
models = bigData.getAndRemove();
也可以使用
bigData.get(),不过这样数据在内存会越存越多,最终导致内存溢出。页面回收恢复后,重复创建 Fragment 导致重复的缓存数据
通过FragmentManager.beginTransaction.add.commit添加Framgnet时,但页面回收恢复后,FragmentManager仍然会保留所有之前的Fragment,所以:
方式一:重复使用缓存的 Fragment,不再重复创建
方式二:在页面回收恢复后,先删除之前的所有 Fragment![]()
ViewPager.setOffscreenPageLimit 太大导致 Fragment 被杀死时缓存的数据过大
如果页面数据很大,我们也应该格外注意 setOffscreenPageLimit,如果 OffscreenPageLimit 的数量过大,应该选择使用本地持久化等方式对页面数据进行缓存。
