Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Parcelable vs Serializable. Что лучше?

Android Parcelable vs Serializable. Что лучше?

Видеозапись доклада https://youtu.be/tko54cjc79U

Разбор способов сериализации на Android и чем отличаются Parcelable и Serializable, а самое главное что лучше и быстрее

Эксперт - Павел Борзиков, Android разработчик в Avito
Начал свою карьеру разработчика в 2014 году в Краснодарском стартапе. Успел набраться опыта в разработке банковского приложения, сервисе доставки еды, а сейчас делает лучше жизнь пользователям Авито.

🔗 Подпишись на канал https://www.youtube.com/channel/UCKsqMPIIhev3qbMxCL8Emvw/join
🔗 Telegram канал "Android Broadcast" https://ttttt.me/android_broadcast

Android Broadcast

October 17, 2021
Tweet

More Decks by Android Broadcast

Other Decks in Programming

Transcript

  1. 2 ТЕРМИНОЛОГИЯ Person Name John Surname Smith Age 21 1

    0 1 1 1 1 0 1 0 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 1 1 1 Сериализация Процесс перевода структуры данных в последовательность байтов.
  2. 3 ТЕРМИНОЛОГИЯ Person Name John Surname Smith Age 21 1

    0 1 1 1 1 0 1 0 1 1 0 1 1 0 0 1 0 0 1 0 1 0 0 1 1 1 Десериализация Создание структуры данных из битовой последовательности.
  3. 4 ПРИМЕР КЛАССА package com.myapplication class Person( private val name:

    String, private val surname: String, private val age: Int, )
  4. 6 СТРУКТУРА КЛАССА И ДАННЫЕ package com.myapplication class Person( private

    val name: String, private val surname: String, private val age: Int, ) : Serializable
  5. 7 ПРИМЕР СЕРИАЛИЗАЦИИ val serializablePerson = Person( name = "John",

    surname = "Smith", age = 21, ) val filePath = "${directory}/serialized.bin" val file = File(filePath).apply { createNewFile() } // write Person val fileOutputStream = FileOutputStream(file) ObjectOutputStream(fileOutputStream).use { it.writeObject(serializablePerson) it.flush() } // read Person val fileInputStream = FileInputStream(file) ObjectInputStream(fileInputStream).use { print(it.readObject() as Person) }
  6. 8 ПРИМЕР СЕРИАЛИЗАЦИИ val serializablePerson = Person( name = "John",

    surname = "Smith", age = 21, ) val filePath = "${directory}/serialized.bin" val file = File(filePath).apply { createNewFile() } // write Person val fileOutputStream = FileOutputStream(file) ObjectOutputStream(fileOutputStream).use { it.writeObject(serializablePerson) it.flush() }
  7. 9 ПРИМЕР СЕРИАЛИЗАЦИИ val serializablePerson = Person( name = "John",

    surname = "Smith", age = 21, ) val filePath = "${directory}/serialized.bin" val file = File(filePath).apply { createNewFile() } // read Person val fileInputStream = FileInputStream(file) ObjectInputStream(fileInputStream).use { print(it.readObject() as Person) }
  8. 10 ПРИМЕР СЕРИАЛИЗАЦИИ val serializablePerson = Person( name = "John",

    surname = "Smith", age = 21, ) val filePath = "${directory}/serialized.bin" val file = File(filePath).apply { createNewFile() } /
  9. 14 Значения. Пакеты класса и переменных; 01. Содержимое serialized.bin Имена

    переменных; 02. 03. АНАЛИЗ СЕРИАЛИЗОВАННЫХ ДАННЫХ ?
  10. 15 Значения. Пакеты класса и переменных; 01. Содержимое serialized.bin Имена

    переменных; 02. 03. АНАЛИЗ СЕРИАЛИЗОВАННЫХ ДАННЫХ ?? sr*com.myapplication.Person ?? T»1? IageLnametLjava/lang/String;L surnameq~xptJohntSmith
  11. 17 ПРИМЕР РЕФЛЕКСИИ val person = Person( name = "John",

    surname = "Smith", age = 21, ) val clazz: Class<Person> = person.javaClass val field: Field = clazz.getDeclaredField("name") field.isAccessible = true println(field.get(person)) println(field.type) println(field.declaringClass)
  12. 18 ПРИМЕР РЕФЛЕКСИИ val person = Person( name = "John",

    surname = "Smith", age = 21, ) val clazz: Class<Person> = person.javaClass val field: Field = clazz.getDeclaredField("name") field.isAccessible = true println(field.get(person)) println(field.type) println(field.declaringClass) val clazz: Class<Person> = person.javaClass
  13. 19 ПРИМЕР РЕФЛЕКСИИ val person = Person( name = "John",

    surname = "Smith", age = 21, ) val clazz: Class<Person> = person.javaClass val field: Field = clazz.getDeclaredField("name") field.isAccessible = true println(field.get(person)) println(field.type) println(field.declaringClass) val field: Field = clazz.getDeclaredField("name")
  14. 20 ПРИМЕР РЕФЛЕКСИИ val person = Person( name = "John",

    surname = "Smith", age = 21, ) val clazz: Class<Person> = person.javaClass val field: Field = clazz.getDeclaredField("name") field.isAccessible = true println(field.get(person)) println(field.type) println(field.declaringClass) println(field.get(person)) println(field.type) println(field.declaringClass) Stdout: John class java.lang.String class com.myapplication.Person
  15. 23 01. Потребление памяти. Во время «исследования» создаются дополнительные объекты;

    02. ПРОБЛЕМА РЕФЛЕКСИИ НА ANDROID Рефлексия медленная. Чтобы обратится к объектам, сначала надо их «исследовать»;
  16. 24 01. Потребление памяти. Во время «исследования» создаются дополнительные объекты;

    02. Частый вызов GC. Следствие большого потребления памяти; 03. ПРОБЛЕМА РЕФЛЕКСИИ НА ANDROID Рефлексия медленная. Чтобы обратится к объектам, сначала надо их «исследовать»;
  17. 25 01. Потребление памяти. Во время «исследования» создаются дополнительные объекты;

    02. Частый вызов GC. Следствие большого потребления памяти; 03. Слабое железо. Непростая задача для первых Android смартфонов. 04. ПРОБЛЕМА РЕФЛЕКСИИ НА ANDROID Рефлексия медленная. Чтобы обратится к объектам, сначала надо их «исследовать»;
  18. 26 01. Потребление памяти. Во время «исследования» создаются дополнительные объекты;

    02. Частый вызов GC. Следствие большого потребления памяти; 03. Слабое железо. Непростая задача для первых Android смартфонов. 04. ПРОБЛЕМА РЕФЛЕКСИИ НА ANDROID Рефлексия медленная. Чтобы обратится к объектам, сначала надо их «исследовать»; И СЕРИАЛИЗАЦИИ
  19. 28 ПРИМЕР PARCELABLE class Person( ... ) : Parcelable {

    override fun describeContents() = 0 override fun writeToParcel(dest:Parcel,flags:Int) { dest.writeString(name) dest.writeString(surname) dest.writeInt(age) } companion object CREATOR : Creator<Person> { override fun createFromParcel(parcel: Parcel) = Person( name = parcel.readString() ! ! , surname = parcel.readString() !! , age = parcel.readInt(), ) override fun newArray(size: Int) = ... } }
  20. 30 ПРИМЕР PARCELABLE class Person( ! ! , surname =

    parcel.readString() !! , age = parcel.readInt(), )
  21. 31 ПРИМЕР PARCELABLE class Person( ! ! , surname =

    parcel.readString() !! , age = parcel.readInt(),
  22. 40 ПРИМЕР PARCELABLE class Person( ... ) : Parcelable {

    override fun describeContents() = 0 override fun writeToParcel(dest:Parcel,flags:Int) { dest.writeString(name) dest.writeString(surname) dest.writeInt(age) } companion object CREATOR : Creator<Person> { override fun createFromParcel(parcel: Parcel) = Person( parcel.readString() !! , parcel.readString() !! , parcel.readInt(), ) override fun newArray(size: Int) = ... } }
  23. 41 ПОЛУЧЕНИЕ БАЙТОВ ИЗ PARCEL val person = Person( name

    = "John", secondName = "Smith", age = 21, ) val source = Parcel.obtain() source.writeParcelable(person, 0) val bytes = source.marshall() parcelSource.recycle() val destination = Parcel.obtain() destination.unmarshall(bytes, 0, bytes.size) destination.setDataPosition(0) val classLoader = Person : : class.java.classLoader val result = destination.readParcelable(classLoader) parcelDestination.recycle() println(result)
  24. 42 ПОЛУЧЕНИЕ БАЙТОВ ИЗ PARCEL val person = Person( name

    = "John", secondName = "Smith", age = 21, ) val source = Parcel.obtain() source.writeParcelable(person, 0) val bytes = source.marshall() parcelSource.recycle() val destination = Parcel.obtain() destination.unmarshall(bytes, 0, bytes.size) destination.setDataPosition(0) val classLoader = Person :
  25. 43 ПОЛУЧЕНИЕ БАЙТОВ ИЗ PARCEL val person = Person( name

    = "John", secondName = "Smith", age = 21, ) val source = Parcel.obtain() source.writeParcelable(person, 0) val bytes = source.marshall() parcelSource.recycle() val destination = Parcel.obtain() destination.unmarshall(bytes, 0, bytes.size) destination.setDataPosition(0) val classLoader = Person : : class.java.classLoader val result = destination.readParcelable(classLoader) println(result)
  26. 45 ПОЛУЧЕНИЕ БАЙТОВ ИЗ PARCEL val person = Person( name

    = "John", secondName = "Smith", age = 21, ) val source = Parcel.obtain() source.writeParcelable(person, 0) val bytes = source.marshall() parcelSource.recycle() val destination = Parcel.obtain() destination.unmarshall(bytes, 0, bytes.size) destination.setDataPosition(0) val classLoader = Person :
  27. 46 ДОКУМЕНТАЦИЯ The data you retrieve here must not be

    placed in any kind of persistent storage (on local disk, across a network, etc). For that, you should use standard serialization or another kind of general serialization mechanism. The Parcel marshalled representation is highly optimized for local IPC, and as such does not attempt to maintain compatibility with data created in different versions of the platform. Parcel.marshall()
  28. 48 val person = Person( name = "John", secondName =

    "Smith", age = 21, ) val source = Parcel.obtain() source.writeParcelable(person, 0) val bytes = source.marshall() parcelSource.recycle() val destination = Parcel.obtain() destination.unmarshall(sourceBytes, 0, bytes.size) destination.setDataPosition(0) val classLoader = Person : : class.java.classLoader val result = destination.readParcelable(classLoader) parcelDestination.recycle() println(result) КАК ЗАМЕРЯЕМ PARCELABLE
  29. 49 КАК ЗАМЕРЯЕМ SERIALIZABLE val serializablePerson = Person( name =

    "John", surname = "Smith", age = 21, ) val outputStream = ByteArrayOutputStream(120) // write Person ObjectOutputStream(outputStream).use { it.writeObject(serializablePerson) it.flush() } // read Person val fileInputStream = FileInputStream(outputStream) ObjectInputStream(fileInputStream).use { print(it.readObject() as Person) }
  30. 51 Устройства: ▶ Pixel 4 ( API 29 ); 


    ▶ Samsung A300F ( API 21 ). КАК ЗАМЕРЯЕМ Среднее время работы с 10 запусков;
  31. 52 Устройства: ▶ Pixel 4 ( API 29 ); 


    ▶ Samsung A300F ( API 21 ). Размер полученного массива байт. КАК ЗАМЕРЯЕМ Среднее время работы с 10 запусков;
  32. 53 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ Parcelable Serializable Память 92 bytes 117 bytes

    Среднее время API 21 1 486 K 12 565 K Среднее время API 29 300 K 2 115 K + 27% + 605% + 745% Наносекунды
  33. 54 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ Parcelable Serializable Память 92 bytes 117 bytes

    Среднее время API 21 1 486 K 12 565 K Среднее время API 29 300 K 2 115 K + 27% + 605% + 745% Наносекунды
  34. 59 val classLoader = Person : : class.java.classLoader val result

    = destination.readParcelable(classLoader) PARCELABLE БЕЗ РЕФЛЕКСИИ?
  35. 72 МЕТАДАННЫЕ КЛАССА val clazz: Class<Person> = Class.forName("com.myapplication.Person", CLASSLOADER?) Parcelizable

    Явно указывается. Serializable Последний ClassLoader в стеке.
  36. 74 ПЕРЕДАЧА ДАННЫХ ЧЕРЕЗ BUNDLE Activity A Activity B Name

    John Surname Smith Bundle Name ... Surname ... Bundle
  37. 76 ДАННЫЕ ЧЕРЕЗ IBINDER Activity A Activity B Name John

    Surname Smith Age 24 Salary $1.000 Job Age City Moscow Pet Dog Rating 4.8 Bundle Bundle Name ... Surname ... Age ... Salary ... Job ... City ... Pet ... Rating ... IBinder
  38. 77 ДАННЫЕ ЧЕРЕЗ IBINDER Activity A Activity B Name John

    Surname Smith Age 24 Salary $1.000 Job Age City Moscow Pet Dog Rating 4.8 Bundle Bundle IBinder Name ... Surname ... Age ... Salary ... Job ... City ... Pet ... Rating ... TransactionTooLargeException max 1 Mb
  39. 79 SERIALIZABLE vs PARCELABLE Serializable Медленнее чем Parcelizable, можно сохранять

    в постоянное хранилище. Parcelizable Быстрая обработка, требует меньше места, используется для транзакций через IBinder. Нельзя использовать в постоянном хранилище. Нет ограничений.
  40. 81 ИЗМЕНЕНИЕ СИГНАТУРЫ КЛАССА ?? sr*com.myapplication.Person ?? T»1? IageLnametLjava/lang/String;L surnameq~xptJohntSmith

    Bytes class Person( val name: String, val surname: String, val age: Int, ) : Serializable class Person( val name: String, val NEW_NAME: String, val age: Int, ) : Serializable . . . . . . . . . . .
  41. 83 ID СИГНАТУРЫ КЛАССА class Person( private val name: String,

    private val surname: String, private val age: Int, ) : Serializable { companion object { const val serialVersionUID = -48629433707L } } companion object { const val serialVersionUID = -48629433707L }
  42. 84 ИЗМЕНЕНИЕ СИГНАТУРЫ КЛАССА ?? sr*com.myapplication.Person ?? T»1? IageLnametLjava/lang/String;L surnameq~xptJohntSmith

    Bytes class Person( val name: String, val surname: String, val age: Int, ) : Serializable class Person( val name: String, val NEW_NAME: String, val age: Int, ) : Serializable . . . . . . . . InvalidClassException
  43. 85 ИЗМЕНЕНИЕ СИГНАТУРЫ КЛАССА class Person( val name: String, val

    NEW_NAME: String, val age: Int, ) : Serializable ?? sr*com.myapplication.Person ?? T»1? IageLnametLjava/lang/String;L surnameq~xptJohntSmith serialVersionUID serialVersionUID ==
  44. 86 ИЗМЕНЕНИЕ СИГНАТУРЫ КЛАССА class Person( val name: String, val

    surname: String, val age: Int, ) : Serializable ?? sr*com.myapplication.Person ?? IageLnametLjava/lang/String;L surnameq~xptJohntSmith 48629433707 70733492684 x
  45. 88 РУЧНАЯ СЕРИАЛИЗАЦИЯ class Person( private val name: String, private

    val surname: String, @Transient private val age: Int, ) : Serializable { companion object { const val serialVersionUID = -48629433707L } } TRANSIENT Поле, не помеченное этим ключевым словом, не будет подвергаться сериализации. @Transient
  46. 91 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ EXTERNALIZABLE Расширение Serializable с обязательным

    переопределением методов чтения и записи объекта. Наличие пустого конструктора обязательно! class Person( private var name: String, private var surname: String, private var age: Int, ) : Externalizable { constructor() : this("", "", 0) override fun writeExternal(out: ObjectOutput) { out.writeObject(name) out.writeObject(surname) out.writeInt(age) } override fun readExternal(`in`: ObjectInput) { name = `in`.readObject() as String surname = `in`.readObject() as String age = `in`.readInt() } }
  47. 92 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ EXTERNALIZABLE Расширение Serializable с обязательным

    переопределением методов чтения и записи объекта. Наличие пустого конструктора обязательно! class Person( private var name: String, private var surname: String, private var age: Int, ) : Externalizable { constructor() : this("", "", 0) override fun writeExternal(out: ObjectOutput) { out.writeObject(name) out.writeObject(surname) out.writeInt(age) } override fun readExternal(`in`: ObjectInput) { name = `in`.readObject() as String surname = `in`.readObject() as String age = `in`.readInt() } } override fun writeExternal(out: ObjectOutput) { out.writeObject(name) out.writeObject(surname) out.writeInt(age) } override fun readExternal(`in`: ObjectInput) { name = `in`.readObject() as String surname = `in`.readObject() as String age = `in`.readInt() }
  48. 93 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ EXTERNALIZABLE Расширение Serializable с обязательным

    переопределением методов чтения и записи объекта. Наличие пустого конструктора обязательно! class Person( private var name: String, private var surname: String, private var age: Int, ) : Externalizable { constructor() : this("", "", 0) override fun writeExternal(out: ObjectOutput) { out.writeObject(name) out.writeObject(surname) out.writeInt(age) } override fun readExternal(`in`: ObjectInput) { name = `in`.readObject() as String surname = `in`.readObject() as String age = `in`.readInt() } } constructor() : this("", "", 0)
  49. 94 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ Parcelable Serializable Externalizable Память 92 bytes 117

    bytes 68 bytes Среднее время API 21 1 486 K 12 565 K 4 189 K Среднее время API 29 300 K 2 115 K 695 K + 27% - 28% + 605% + 131% + 181% + 745%
  50. 96 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ WRITEOBJECT / READOBJECT Ручное управление

    чтения/ записи с возможностью расширения чтения/записи по умолчанию. class Person( private var name: String, private var surname: String, private var age: Int, ) : Serializable { private fun writeObject(out: ObjectOutputStream) { out.defaultWriteObject() out.writeInt(42) } private fun readObject(`in`: ObjectInputStream) { `in`.defaultReadObject() `in`.readInt() / / 42 }
  51. 97 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ WRITEOBJECT / READOBJECT Ручное управление

    чтения/ записи с возможностью расширения чтения/записи по умолчанию. class Person( private var name: String, private var surname: String, private var age: Int, ) : Serializable { private fun writeObject(out: ObjectOutputStream) { out.defaultWriteObject() out.writeInt(42) } private fun readObject(`in`: ObjectInputStream) { `in`.defaultReadObject() `in`.readInt() /
  52. 98 БОЛЕЕ ДЕТАЛЬНОЕ УПРАВЛЕНИЕ СЕРИАЛИЗАЦИЕЙ WRITEOBJECT / READOBJECT Ручное управление

    чтения/ записи с возможностью расширения чтения/записи по умолчанию. class Person( private var name: String, private var surname: String, private var age: Int, ) : Serializable { private fun writeObject(out: ObjectOutputStream) { out.defaultWriteObject() out.writeInt(42) } private fun readObject(`in`: ObjectInputStream) { `in`.defaultReadObject() `in`.readInt() / / 42