Первое, что я попробовал, - это вернуться к сериализации Java, и, конечно же, все сработало. Затем я попытался достичь предела этого подхода и потерпел неудачу, даже когда создал график в 100 раз больше, чем тот, который вызвал исходный сбой, и во много раз больше, чем сценарий наихудшего случая, который я мог вообразить. Еще до того, как эта проблема возникла, я обнаружил, что комбинация Parcelable / Parceler не сохраняет ссылки на один и тот же объект в графе при демаршалировании, а создает копии объекта столько раз, сколько на него ссылается исходный граф. Хотя такое поведение может быть не очень удивительным или совсем не удивительным, оно отличается от того, как работает магия Serializable.

Чтобы проверить это в более контролируемой среде, я создал небольшое экспериментальное приложение. Вы можете найти apk здесь и исходник здесь. В нем вы найдете класс EmptyObject, и это просто класс без переменных экземпляра любого типа, фактически без какого-либо кода. Я хотел посмотреть, сколько из этих бесполезных ублюдков я мог бы втиснуть в ArrayList и перенести из одного действия в другое, используя сериализацию Java или Parcelable / Parceler. Я также создал класс под названием Package, содержащий ссылку на ArrayList ‹EmptyObject› и ничего больше. Экземпляр класса Package - это единственное, что я помещаю в Intent и начинаю с ним другое действие.

Результаты в

Механизм Parcelable / Parceler может передавать пакет из 4 045 экземпляров EmptyObject. Когда экземпляр Package маршалируется, результирующий байт [] имеет длину 517 764 байта. Добавление еще одного экземпляра EO в микс приводит к сбою приложения с TransactionTooLargeException, поэтому я полагаю, что 517 892 байта - это слишком много. В то же время пакет из 4 045 экземпляров EO имеет длину всего 24 482 байта, когда он сериализован в byte [], поэтому его можно легко передать. Предел при использовании сериализации Java составляет 86 276 экземпляров EO в пакете, создавая сериализованный байт [] размером 517 928 байт. Я предполагал, что если я создам список со всеми ссылками, указывающими только на один экземпляр EO, это число будет намного больше, но на самом деле это не так. Пакет, содержащий список из 86 276 ссылок EO на один экземпляр при сериализации в byte [], имеет 431 593 байта, поэтому мы можем втиснуть туда еще 20 КБ или около того :) Понятно, что независимо от используемой технологии лимит - это количество байты можно передавать. На моем Moto X (2-го поколения) под управлением Android 5.0 это число немного меньше 512 КБ. Результаты зависят от устройства. Я заметил, что на других устройствах поведение варьируется от немного лучшего на Galaxy S5 и S4, они могут передавать на пару КБ больше, до почти полного МБ на HTC One M8. Он не выглядит связанным с исполняемой средой, которую использует телефон (dalvik / ART). Ясно одно: сериализация java создает намного меньший байт [], чем использование комбинации Parcelable / Parceler на том же графе объектов. .

Дерьмо становится странным

Может быть, вам интересно, зачем мне нужен класс Package, почему я не отправил ArrayList ‹EmptyObject› напрямую. Для упрощения я решил сделать именно это. И все взорвалось. Внезапно я не смог отправить 86 КБ объектов с помощью java-сериализации, я даже не смог отправить жалкие 4 КБ с помощью Parcelable / Parceler. И что еще более странно, если бы я отправил список с 10 ссылками на один и тот же объект, я бы получил 10 разных объектов на другой стороне. Я понятия не имел, что происходит.

Поскольку ArrayList реализует интерфейс Serializable, и он также полон сериализуемых экземпляров EmptyObject, я думал, что вызываю Intent.putExtra(String name, Serializable value) разновидность методов putExtra. И я был, но он делал не то, что я ожидал. Если вы посмотрите на код этого метода, вы увидите, что он на самом деле вызывает метод putSerializable(String key, Serializable value) класса Bundle (BaseBundle из уровня API 21), и все, что делает этот метод, - это выгружает объект Serializable на карту под заданным Ключ имени строки. Эта карта будет фактически упорядочена методом writeArrayMapInternal(ArrayMap<String, Object> val) из класса Parcel. Вот тут-то и становится интересно. Этот метод будет выполнять итерацию на указанной карте и вызывать метод writeValue(Object v) для каждого объекта в ней, метод, который является всего лишь прославленным оператором switch, и он будет выполнять множество проверок instanceof и вызывать методы writeThis и writeThat. Фактически, наш список будет написан не методом writeSerializable(Serializable s), а методом writeList(List val), выполняющим итерацию и выполняющую отдельную запись writeSerializable для элементов списка. Вот и все: мы передали сериализуемый объект методу putExtra от Intent, который также является списком, и ожидали, что он сериализует все это за один раз, но вместо этого он сам сериализует каждый экземпляр в списке.

Хорошо, но Parcelable по-прежнему в 10 раз быстрее, верно?

На самом деле я не знаю, поскольку я не собираюсь начинать реализацию чистых классов Parcelable. Однако приложение для экспериментов измеряет время от создания намерения в действии A до его распаковки в действии B, хотя, возможно, наивно. Я не проводил тест 1000 раз, чтобы получить среднее значение, но мне кажется, что в этом сценарии не произойдет улучшения в 10 или 17 раз по сравнению с сериализацией Java. Да, похоже, что комбинация Parcelable / Parceler работает быстрее, но, скорее, в 2 раза быстрее. Но послушайте, я отправляю сюда кучу пустых объектов. Помните граф объектов из реального приложения, с которого все это началось? На самом деле это примерно в 3 раза быстрее, если использовать Java-сериализацию, и я подтвердил это на множестве устройств. Я не знаю почему, и сейчас боюсь спросить.

Заключение

Я не собираюсь возвращаться к Java-сериализации. Возможно, я здесь что-то делаю не так, а может быть, это как-то связано с тем, что устройства Android сейчас совсем другие, чем те, что были всего пару лет назад. Мой единственный совет: не доверяйте слепо всему, что вы читаете в Интернете, и пробуйте свои собственные жизненные сценарии. Это должно позволить вам сделать важные выводы.