Fix-Android-TransactionTooLargeException

介绍

TransactionTooLargeException 表示在 RPC 调用中传输的数据过大(超过 1MB), 导致 RPC 调用失败.

TooLargeTool 是一款可以方便打开 Bundle 大小的工具, 可以借鉴里面的代码实现.

背景

除了常见的 Bundle 传输数据过大导致 startActivity 失败外, 经常还见到另一个堆栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Caused by:
android.os.TransactionTooLargeException:data parcel size 942884 bytes
android.os.BinderProxy.transactNative(Native Method)
android.os.BinderProxy.transact(BinderProxy.java:621)
android.app.IActivityClientController$Stub$Proxy.activityStopped(IActivityClientController.java:1459)
android.app.ActivityClient.activityStopped(ActivityClient.java:112) android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:135)
android.os.Handler.handleCallback(Handler.java:958)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loopOnce(Looper.java:224)
android.os.Looper.loop(Looper.java:318)
android.app.ActivityThread.main(ActivityThread.java:8669)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)

同时 App 为单 Activity 多 Fragment 架构

复现路径

我们找到了一个复现路径:

  1. 同一个Activity打开多个 Fragment, 尽量不少于 20 个
  2. App 退后台
  3. 此时可以在 Logcat 看到 TransactionTooLargeException

排查过程

  1. 查看代码, 退后台时会调用 Fragment 的 onSaveInstanceState(Bundle) , 这是用来保存状态的方法, 尝试在 Fragment 的 onSaveInstanceState 方法中打印 Bundle 的大小, 结果只有大概 1kb .

  2. 除了 Fragment 的 `onSaveInstanceState ,还有 Activity 的 onSaveInstanceState 方法, 打印出来. 两个 Fragment 大约有 100kb, 如果再开一个 Fragment, 还会增加 70kb. 以此类推, 大约16个 Fragment, 就会超过 1MB 的限制. 所以原因就是** Activity 调用 onSaveInstanceState 时 Bundle 太大了**.

    1
    2
    3
    4
    5
    6
    activity onSaveInstanceState: com.example.activity.MainActivity@7db60c7 size = Bundle62666159 contains 5 keys and measures 108.3 KB when serialized as a Parcel
    * com.google.app_measurement.screen_service = 0.2 KB
    * android:viewHierarchyState = 0.3 KB
    * androidx.lifecycle.BundlableSavedStateRegistry.key = 107.3 KB
    * android:lastAutofillId = 0.1 KB
    * android:fragments = 0.4 KB bundle size 5

    Bundle 中什么东西占用了空间

    通过查看源码和结合工具打印, Bundle 本质也是 Key-Value 的形式, 我们可以一层一层解析 Bundle 中的数据和大小.

  3. androidx.lifecycle.BundlableSavedStateRegistry.key 存放的是 Bundle, 里面占用大小最大的是 android:support:fragments 这个 key 存放的 Bundle.
    源码:

    日志:

    1
    2
    3
    4
    5
    6
    androidx.lifecycle.BundlableSavedStateRegistry.key size = 101 KB , map size 5
    key: androidx:appcompat size = 4 B
    key: android:support:lifecycle size = 4 B
    key: androidx.lifecycle.internal.SavedStateHandlesProvider size = 4 B
    key: android:support:activity-result size = 7.4 KB bundle
    key: android:support:fragments size = 93 KB bundle map size 3
  4. android:support:fragments key 中存放的是 Bundle, 里面是每个 Fragment 的 FragmentState 对象.
    源码:

    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
    ​``` FragmentManager
    public abstract class FragmentManager implements FragmentResultOwner {
    static final String SAVED_STATE_TAG = "android:support:fragments";

    // 找到 android:support:fragments 的 key
    SavedStateRegistry registry =
    ((SavedStateRegistryOwner) mHost).getSavedStateRegistry();
    registry.registerSavedStateProvider(SAVED_STATE_TAG, () -> {
    return saveAllStateInternal();
    }
    );

    // 找到 fragment_ 的key
    static final String FRAGMENT_STATE_TAG = "state";
    static final String FRAGMENT_NAME_PREFIX = "fragment_";
    private final FragmentStore mFragmentStore = new FragmentStore();

    // saveAllStateInternal() 方法
    @NonNull
    Bundle saveAllStateInternal() {
    ...
    // First save all active fragments.
    ArrayList<String> active = mFragmentStore.saveActiveFragments();

    ...
    for (FragmentState state : savedState) {
    Bundle fragmentBundle = new Bundle();
    fragmentBundle.putParcelable(FRAGMENT_STATE_TAG, state);
    bundle.putBundle(FRAGMENT_NAME_PREFIX + state.mWho, fragmentBundle);
    }

    return bundle;
    }
    }

    // FragmentStore
    class FragmentStore {
    private final HashMap<String, FragmentState> mSavedState = new HashMap<>();

    @NonNull
    ArrayList<FragmentState> getAllSavedState() {
    return new ArrayList<>(mSavedState.values());
    }
    }

    日志:

    1
    2
    3
    4
    android:support:fragments size = 93 KB bundle map size 3
    key: fragment_cc246ae6-d05a-44c1-9a07-e6b6e466cd03 size = 40 KB
    key: fragment_98067ccf-09ad-415b-b937-bb79c097db81 size = 52 KB

  5. FragmentState 可以直接在源码中找到, 由于其他都是基础类型, 最大的怀疑对象只有 mSavedFragmentState.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    final class FragmentState implements Parcelable {
    final String mClassName;
    final String mWho;
    final boolean mFromLayout;
    final int mFragmentId;
    final int mContainerId;
    final String mTag;
    final boolean mRetainInstance;
    final boolean mRemoving;
    final boolean mDetached;
    final Bundle mArguments;
    final boolean mHidden;
    final int mMaxLifecycleState;

    Bundle mSavedFragmentState;
    }

    尝试反射打印一下大小也能印证:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    name: mArguments              size 4 B
    name: mClassName size 49 B
    name: mContainerId size 82 B
    name: mDetached size 48 B
    name: mFragmentId size 82 B
    name: mFromLayout size 48 B
    name: mHidden size 48 B
    name: mMaxLifecycleState size 82 B
    name: mRemoving size 48 B
    name: mRetainInstance size 48 B
    name: mSavedFragmentState size 40 KB
  6. mSavedFragmentState 因为也是 Bundle, 可以打印里面的 value 的大小.

    1
    2
    3
    4
    5
    6
    7
    onSaveInstanceState of: Bundle121352603 contains 6 keys and measures 57.4 KB 
    * fragmentation_arg_container = 0.1 KB
    * android:support:fragments = 16.3 KB
    * fragmentation_invisible_when_leave = 0.1 KB
    * fragmentation_state_save_animator = 0.2 KB
    * androidx.lifecycle.BundlableSavedStateRegistry.key = 0.2 KB
    * android:view_state = 40.5 KB

    源码可以看到 android:view_state 就是一个 SparseParcelableArray

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class FragmentStateManager {
    private static final String VIEW_STATE_TAG = "android:view_state";

    private Bundle saveBasicState() {
    Bundle result = new Bundle();
    // Fragment 保存状态
    mFragment.performSaveInstanceState(result);
    ...
    if (mFragment.mSavedViewState != null) {
    if (result == null) {
    result = new Bundle();
    }
    result.putSparseParcelableArray(
    VIEW_STATE_TAG, mFragment.mSavedViewState);
    }
    ...
    return result;
    }
    }
  7. 打印 android:view_state , Array 中存放的数据比较多

    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
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    index: 9 value: CompoundButton.SavedState{5d3a096 checked=false} size 212 B
    index: 10 value: CompoundButton.SavedState{d4417 checked=false} size 212 B
    index: 11 value: CompoundButton.SavedState{12f3004 checked=false} size 212 B
    index: 12 value: CompoundButton.SavedState{8d0d9ed checked=false} size 212 B
    index: 13 value: CompoundButton.SavedState{da13022 checked=false} size 212 B
    index: 14 value: CompoundButton.SavedState{6980ab3 checked=false} size 212 B
    index: 15 value: CompoundButton.SavedState{b714870 checked=false} size 212 B
    index: 16 value: CompoundButton.SavedState{9ee27e9 checked=false} size 212 B
    index: 17 value: CompoundButton.SavedState{1c62c6e checked=false} size 212 B
    index: 18 value: CompoundButton.SavedState{dfe9f0f checked=false} size 212 B
    index: 19 value: CompoundButton.SavedState{e7a7b9c checked=false} size 212 B
    index: 20 value: CompoundButton.SavedState{27859a5 checked=false} size 212 B
    index: 21 value: CompoundButton.SavedState{32ca17a checked=false} size 212 B
    index: 22 value: CompoundButton.SavedState{1901d2b checked=false} size 212 B
    index: 23 value: CompoundButton.SavedState{d81b588 checked=false} size 212 B
    index: 24 value: CompoundButton.SavedState{4b9eb21 checked=false} size 212 B
    index: 25 value: CompoundButton.SavedState{5535b46 checked=false} size 212 B
    index: 26 value: CompoundButton.SavedState{8716107 checked=false} size 212 B
    index: 27 value: CompoundButton.SavedState{640a234 checked=false} size 212 B
    index: 28 value: CompoundButton.SavedState{329185d checked=false} size 212 B
    index: 29 value: CompoundButton.SavedState{2d9e5d2 checked=false} size 212 B
    index: 30 value: CompoundButton.SavedState{d3906a3 checked=false} size 212 B
    index: 31 value: CompoundButton.SavedState{65fada0 checked=false} size 212 B
    index: 32 value: CompoundButton.SavedState{763dd59 checked=false} size 212 B
    index: 33 value: CompoundButton.SavedState{ecc8d1e checked=false} size 212 B
    index: 34 value: CompoundButton.SavedState{34b69ff checked=false} size 212 B
    index: 35 value: CompoundButton.SavedState{ba203cc checked=false} size 212 B
    index: 36 value: CompoundButton.SavedState{cebf615 checked=false} size 212 B
    index: 37 value: CompoundButton.SavedState{eb05d2a checked=false} size 212 B
    index: 38 value: CompoundButton.SavedState{5f6a71b checked=false} size 212 B
    index: 39 value: CompoundButton.SavedState{ad190b8 checked=false} size 212 B
    index: 40 value: CompoundButton.SavedState{ba2de91 checked=false} size 212 B
    index: 41 value: CompoundButton.SavedState{94f21f6 checked=false} size 212 B
    index: 42 value: CompoundButton.SavedState{d4e99f7 checked=false} size 212 B
    index: 43 value: CompoundButton.SavedState{96b0064 checked=false} size 212 B
    index: 44 value: CompoundButton.SavedState{e05d2cd checked=false} size 212 B
    index: 45 value: CompoundButton.SavedState{6436782 checked=false} size 212 B
    index: 46 value: CompoundButton.SavedState{4c8de93 checked=false} size 212 B
    index: 47 value: CompoundButton.SavedState{609bed0 checked=false} size 212 B
    index: 48 value: CompoundButton.SavedState{f29cec9 checked=false} size 212 B
    index: 49 value: CompoundButton.SavedState{a4479ce checked=false} size 212 B
    index: 50 value: CompoundButton.SavedState{f98d0ef checked=false} size 212 B
    index: 51 value: CompoundButton.SavedState{b93f7fc checked=false} size 212 B
    index: 52 value: CompoundButton.SavedState{c778e85 checked=true} size 212 B
    index: 53 value: CompoundButton.SavedState{c3264da checked=false} size 212 B
    index: 54 value: CompoundButton.SavedState{6cb8d0b checked=false} size 212 B
    index: 55 value: CompoundButton.SavedState{b2697e8 checked=false} size 212 B
    index: 56 value: CompoundButton.SavedState{7278e01 checked=false} size 212 B
    index: 57 value: CompoundButton.SavedState{ee1f4a6 checked=false} size 212 B
    index: 58 value: CompoundButton.SavedState{d23eee7 checked=false} size 212 B
    index: 59 value: CompoundButton.SavedState{8c14a94 checked=false} size 212 B
    index: 60 value: CompoundButton.SavedState{17e093d checked=false} size 212 B
    index: 61 value: CompoundButton.SavedState{ba8b532 checked=false} size 212 B
    index: 62 value: CompoundButton.SavedState{4b69283 checked=false} size 212 B
    index: 63 value: CompoundButton.SavedState{6b27c00 checked=false} size 212 B
    index: 64 value: CompoundButton.SavedState{3c6fc39 checked=false} size 212 B
    index: 65 value: CompoundButton.SavedState{9a8f27e checked=false} size 212 B
    index: 66 value: CompoundButton.SavedState{245d3df checked=false} size 212 B
    index: 67 value: CompoundButton.SavedState{bc3582c checked=false} size 212 B
    index: 68 value: CompoundButton.SavedState{51222f5 checked=false} size 212 B
    index: 69 value: CompoundButton.SavedState{dddb88a checked=false} size 212 B
    index: 70 value: CompoundButton.SavedState{a5dcefb checked=false} size 212 B
    index: 71 value: CompoundButton.SavedState{23cb18 checked=false} size 212 B
    index: 72 value: CompoundButton.SavedState{3aef971 checked=false} size 212 B
    index: 73 value: CompoundButton.SavedState{fe6d356 checked=false} size 212 B
    index: 74 value: CompoundButton.SavedState{4305fd7 checked=false} size 212 B
    index: 75 value: CompoundButton.SavedState{d1680c4 checked=false} size 212 B
    index: 76 value: CompoundButton.SavedState{968bbad checked=false} size 212 B
    index: 77 value: CompoundButton.SavedState{f94cee2 checked=false} size 212 B
    index: 78 value: CompoundButton.SavedState{e312273 checked=false} size 212 B
    index: 79 value: CompoundButton.SavedState{b5ce530 checked=false} size 212 B
    index: 80 value: CompoundButton.SavedState{78265a9 checked=false} size 212 B
    index: 81 value: CompoundButton.SavedState{734f72e checked=false} size 212 B
    index: 82 value: CompoundButton.SavedState{7172cf checked=false} size 212 B
    index: 83 value: CompoundButton.SavedState{c63245c checked=false} size 212 B
    index: 84 value: CompoundButton.SavedState{672b365 checked=false} size 212 B
    index: 85 value: CompoundButton.SavedState{69d583a checked=false} size 212 B
    index: 86 value: CompoundButton.SavedState{8bc6ceb checked=true} size 212 B
    index: 87 value: CompoundButton.SavedState{a2c2a48 checked=false} size 212 B
    index: 88 value: CompoundButton.SavedState{56020e1 checked=false} size 212 B
    index: 89 value: CompoundButton.SavedState{2f8be06 checked=false} size 212 B
    index: 90 value: CompoundButton.SavedState{c72ecc7 checked=false} size 212 B
    index: 91 value: CompoundButton.SavedState{9fda2f4 checked=false} size 212 B
    index: 92 value: CompoundButton.SavedState{55cea1d checked=false} size 212 B
    index: 93 value: CompoundButton.SavedState{752b492 checked=false} size 212 B
    index: 94 value: CompoundButton.SavedState{c278e63 checked=false} size 212 B
    index: 95 value: CompoundButton.SavedState{dcbfa60 checked=false} size 212 B
    index: 96 value: CompoundButton.SavedState{c630b19 checked=false} size 212 B
    index: 97 value: CompoundButton.SavedState{1e387de checked=false} size 212 B
    index: 98 value: CompoundButton.SavedState{4faadbf checked=false} size 212 B
    index: 99 value: CompoundButton.SavedState{665c8c checked=false} size 212 B
    index: 100 value: CompoundButton.SavedState{c103fd5 checked=false} size 212 B
    index: 101 value: CompoundButton.SavedState{a1c43ea checked=false} size 212 B
    index: 102 value: CompoundButton.SavedState{bb666db checked=false} size 212 B
    index: 103 value: CompoundButton.SavedState{862b578 checked=false} size 212 B
    index: 104 value: CompoundButton.SavedState{8220451 checked=false} size 212 B
    index: 105 value: CompoundButton.SavedState{b72b4b6 checked=false} size 212 B
    index: 106 value: CompoundButton.SavedState{daa95b7 checked=false} size 212 B
    index: 107 value: CompoundButton.SavedState{dc9b124 checked=false} size 212 B
    index: 108 value: CompoundButton.SavedState{8b1948d checked=false} size 212 B
    index: 109 value: CompoundButton.SavedState{ed6642 checked=false} size 212 B
    index: 110 value: CompoundButton.SavedState{348d653 checked=false} size 212 B
    index: 111 value: CompoundButton.SavedState{e82bb90 checked=false} size 212 B
    index: 112 value: CompoundButton.SavedState{42fec89 checked=false} size 212 B
    index: 113 value: CompoundButton.SavedState{d6fa48e checked=false} size 212 B
    index: 2131427585 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427586 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427590 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427593 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427598 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427802 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427846 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131427848 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428063 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428202 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428413 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428417 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428708 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428779 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428825 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428835 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428906 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428907 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428909 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428910 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131428911 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429227 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429229 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429267 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429275 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429278 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429311 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429320 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429738 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429739 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429740 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429898 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131429900 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430063 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430064 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430125 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430163 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430164 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430167 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430169 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430170 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430171 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430172 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430345 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430360 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430362 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430428 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430442 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430607 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430664 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430668 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430669 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430703 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430842 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430851 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430853 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430905 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430908 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430909 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131430910 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431039 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431050 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431070 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431386 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431572 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131431609 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131432508 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131432857 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131432984 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433180 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433181 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433191 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433196 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433204 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433205 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433790 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131433829 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434147 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434173 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434226 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434416 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434417 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434418 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434419 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434420 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434421 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434422 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434429 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434430 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434431 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434432 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434433 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434434 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434435 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434443 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434444 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434445 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434446 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434447 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434448 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434449 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434450 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434451 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434468 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434469 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434810 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434828 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434835 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434848 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131434968 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435021 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435050 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435129 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435286 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435294 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435455 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435466 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435526 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435880 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131435998 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436152 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436176 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436177 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436178 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436680 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436789 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436791 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436929 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131436931 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131437119 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131437153 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131437154 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131437155 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131437837 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131438026 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131438505 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131438874 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439071 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439124 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439161 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439162 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439163 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439165 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439190 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439210 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439213 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439246 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439648 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439692 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439741 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439790 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439839 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439840 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439962 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439964 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439970 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439973 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439974 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439975 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439976 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439977 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439978 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439979 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439980 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439981 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439982 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439983 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439984 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439985 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131439986 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440068 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440074 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440103 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440153 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440171 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440239 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440562 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131440686 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131441148 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131441578 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131441589 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131441992 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442003 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442375 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442378 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442382 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442383 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442598 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442726 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442795 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131443036 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131443037 value: android.view.AbsSavedState$1@8084af size 68 B
    index: 2131442292 value: androidx.recyclerview.widget.RecyclerView$SavedState@203fa43 size 316 B
    index: 2131439968 value: androidx.recyclerview.widget.RecyclerView$SavedState@a7dbafd size 316 B
    index: 2131432685 value: androidx.recyclerview.widget.RecyclerView$SavedState@d21c845 size 316 B
    index: 2131435295 value: androidx.recyclerview.widget.RecyclerView$SavedState@f9ba3c1 size 316 B
    index: 2131435292 value: androidx.coordinatorlayout.widget.CoordinatorLayout$SavedState@8aa6ca8 size 212 B
    index: 2131429908 value: FragmentPager.SavedState{d8000bc position=0} size 176 B
    index: 2131435148 value: FragmentPager.SavedState{edabccb position=0} size 176 B
    index: 2131435999 value: FragmentPager.SavedState{96fb766 position=0} size 176 B
    index: 2131440220 value: androidx.appcompat.widget.Toolbar$SavedState@32fe3f2 size 172 B
    index: 2131442599 value: HorizontalScrollView.SavedState{4c428c0 scrollPosition=0} size 128 B
    index: 2131435051 value: HorizontalScrollView.SavedState{cc57b9a scrollPosition=0} size 128 B
    index: 2131436299 value: android.widget.ProgressBar$SavedState@d565aa7 size 112 B
    index: 2131436874 value: android.widget.ProgressBar$SavedState@18dab54 size 112 B

    一共 310 个对象, 全部加起来的话
    105 * 212 + 192 * 68 + 4 * 316 + 212 + 3 * 176 + 172 + 128 * 2 + 112 * 2 = 37972 / 1024 = 37kb

原因总结

  1. View 会保存自己的状态, 用于状态恢复.
  2. Fragment 会保存所有 View 的状态(一个 Fragment 大约40k).
  3. Activity 会保存 Activity 下 attach 的所有 Fragment 的状态.
  4. 如果 Activity 下嵌套 Fragment 过多, 保存状态时容易超过 1MB.

解决方法

  1. 不保存 Fragment 的 view 状态, 也不做恢复

    即清除 Bundle 中数据

    注意这里由于 Fragment 先调用 onSaveInstanceState 后保存 view_state, 所以我们要在 Activity 的 onSaveInstanceState 中清除 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
    59
    60
    61
    62
    63
    64
       class BundleClipHelper {

    companion object {

    private const val TAG = "BundleClipHelper"

    /**
    * Bundle 中这两个 key 比较占内存, 暂时只删除这两个 key
    */
    private const val BUNDLE_KEY_FRAGMENTS = "android:support:fragments"
    private const val BUNDLE_KEY_VIEWS = "android:view_state"

    }

    /**
    * 待清理 Bundle 列表
    */
    private val pendingClearBundleList = mutableListOf<Bundle>()

    /**
    * fragment 生命周期回调
    * 在 Fragment 调用 onSaveInstanceState 后记录需要清理的 Bundle
    */
    private val fragmentLifecycleCallbacks: FragmentManager.FragmentLifecycleCallbacks =
    object : FragmentManager.FragmentLifecycleCallbacks() {
    override fun onFragmentSaveInstanceState(fm: FragmentManager, f: Fragment, outState: Bundle) {
    super.onFragmentSaveInstanceState(fm, f, outState)
    pendingClearBundleList.add(outState)
    }
    }

    /**
    * 注册 fragment 生命周期回调
    */
    fun register(activity: FragmentActivity?) {
    activity?.supportFragmentManager?.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
    }

    /**
    * 反注册 fragment 生命周期回调
    */
    fun unRegister(activity: FragmentActivity?) {
    activity?.supportFragmentManager?.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks)
    }

    /**
    * 裁剪 Bundle
    * 在 Activity 的 onSaveInstanceState 中调用
    */
    fun clipBundle(activity: Activity<*>?) {
    if (activity == null || !activity.needClipBundleWhenSaveInstanceState()) {
    pendingClearBundleList.clear()
    return
    }
    if (pendingClearBundleList.isNotEmpty()) {
    Log.i(TAG, "need clear fragment size: " + pendingClearBundleList.size)
    pendingClearBundleList.forEach {
    it.remove(BUNDLE_KEY_FRAGMENTS)
    it.remove(BUNDLE_KEY_VIEWS)
    }
    pendingClearBundleList.clear()
    }
    }
    }
  2. 保存 Bundle 到内存, 并在 Activity 创建前替换, 缺点是不适用于首页(首页重启进程的话内存缓存也会丢失). 可以参考 掘金

  3. 改为单 Activity 框架