Как работает Parcelable?

Я хотел сохранить сложный объект Java во время поворота экрана, поэтому я сделал объект Parcelable и реализовал необходимые методы:

  1. в методе writeToParcel(Parcel dest, int flags) я сохранил некоторые значения в "dest".
  2. в методе Parcelable.Creator createFromParcel(источник Parcel) я получил значения из "источника" в правильном порядке и возвратил соответствующий объект.

Затем в onSaveInstanceState фрагмента я сохранил Parcelable:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable("myObject", myObject);
}

и получил мой объект в onCreate фрагмента:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  MyObject myObject = savedInstanceState.getParcelable("myObject");
}

Это сработало отлично.

Затем я сделал тест:

  1. Удалил весь мой код в методе writeToParcel.
  2. Возвращено значение null в методе createFramParcel.

Когда я запустил приложение, я получил ТОЧНЫЙ ЖЕ РЕЗУЛЬТАТ! Я получил объект со всеми соответствующими значениями в нем.

Почему это сработало? Parcelable создает "Parcelable объекты" автоматически?

1 ответ

Решение

Да, так что это связано с тем, как Bundle обрабатывает внутреннее кэширование и распределение. Когда вы звоните putParcelable(), он запускает следующий код:

public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
    unparcel();
    mMap.put(key, value);
    mFdsKnown = false;
}

Таким образом, в основном, данные в Bundle не сразу записывается в Parcel - mMap является ArrayMap<String, Object> и он содержит кэш всех объектов в Bundle как они вставлены или удалены.

В какой-то момент, writeToParcel() будет вызван на Bundleв этот момент все в mMap записывается в mParcelledData,

Таким образом, в основном, когда вы делаете изменение конфигурации, Bundle до сих пор не было написано Parcelтаким образом, тот же экземпляр объекта, который вы передали, все еще сохраняется в Bundle"s mMap (так что ваш объект также никогда не имел writeToParcel() вызвано - вы можете подтвердить это, утверждая, что объект до и после изменения конфигурации имеет одинаковый System.identityHashCode()).

Вы можете увидеть заметки об этом в BaseBundle:

// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)

ArrayMap<String, Object> mMap = null;

/*
 * If mParcelledData is non-null, then mMap will be null and the
 * data are stored as a Parcel containing a Bundle.  When the data
 * are unparcelled, mParcelledData willbe set to null.
 */
Parcel mParcelledData = null;

Так что, если вы должны были написать свой Parcelable возражать против пакета сохранения состояния и помещать приложение в фоновом режиме до тех пор, пока процесс не прекратится (или я полагаю, что вы можете форсировать это, запустив adb shell am kill <application_id>), а затем возобновить, вы столкнетесь с проблемой, когда ваши данные не правильно распределены.

Другие вопросы по тегам