处理TransactionTooLargeException

  • ~2.72K 字

最近在某些项目上出现了TransactionTooLargeException,查看崩溃日志后返现并没有给出确切的错误定位。

产生原因

TransactionTooLargeException原因分析:在应用层与各种SystemService交互过程中,调用参数和返回值会通过Parcel对象存储在Binder Transaction缓冲区中以进行传输,如果参数或返回值太大超过了Binder`` Transaction缓冲区的限制大小,那么调用将会失败抛出TransactionTooLargeException。在日常开发中,TransactionTooLargeException通常是由于都是传输的数据过大页面被杀死时缓存的数据过大导致的。

定位问题

我们使用toolargetool来定位问题

1
2
3
4
dependencies {
...
implementation 'com.gu.android:toolargetool:0.3.0'
}

Application.onCreate方法中初始化

1
TooLargeTool.startLogging(this);

使用adb命令打开专用监听窗口

1
adb logcat -s TooLargeTool

出现组件正在向事务写入大量数据和缓冲区的时候就会出现日志

常见的原因

  1. Fragment.setArguments传输的Bundle过大

    我们应特别注意通过Fragment.setArguments所传递的Bundle的数据大小,如果数据量较大,应该选择使用其他方式进行传输。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    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);
    }
    }

    存储:

    1
    2
    Bundle args = new Bundle();
    args.putSerializable(MODELS,MemoryBigData.of(models));

    获取:

    1
    2
    MemoryBigData<List<TestDetectionEditViewModel>> bigData = (MemoryBigData) arguments.getSerializable(MODELS);
    models = bigData.getAndRemove();

    也可以使用bigData.get(),不过这样数据在内存会越存越多,最终导致内存溢出。

  2. 页面回收恢复后,重复创建Fragment导致重复的缓存数据
    通过FragmentManager.beginTransaction.add.commit添加Framgnet时,但页面回收恢复后,FragmentManager仍然会保留所有之前的Fragment,所以:
    方式一:重复使用缓存的Fragment,不再重复创建
    方式二:在页面回收恢复后,先删除之前的所有Fragment

  3. ViewPager.setOffscreenPageLimit太大导致Fragment被杀死时缓存的数据过大

    如果页面数据很大,我们也应该格外注意setOffscreenPageLimit,如果OffscreenPageLimit的数量过大,应该选择使用本地持久化等方式对页面数据进行缓存。

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