В этом уроке:
- знакомимся с Parcel
Сам по себе Parcel мне никогда еще использовать не приходилось и не знаю, придется ли. Меня он заинтересовал, когда я начал разбираться с интерфейсом Parcelable. Этот интерфейс используется при передаче объектов через Intent и мне стало интересно, как создавать свои объекты с поддержкой такой передачи. В итоге я немного разобрался в Parcel и Parcelable, хотя понял далеко не все. Попробую теперь рассказать об этом.
Parcel – это контейнер для передачи данных. У него есть куча методов для помещения и извлечения данных. В этом уроке рассмотрим самые простейшие из них.
Создадим проект:
Project name: P0681_Parcel
Build Target: Android 2.3.3
Application name: Parcel
Package name: ru.startandroid.develop.p0681parcel
Create Activity: MainActivity
В этом уроке экран нам не понадобится, main.xml оставляем без изменений. Работать будем с логом.
Кодим в MainActivity.java:
package ru.startandroid.develop.p0681parcel; import android.app.Activity; import android.os.Bundle; import android.os.Parcel; import android.util.Log; public class MainActivity extends Activity { final String LOG_TAG = "myLogs"; Parcel p; /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); writeParcel(); readParcel(); } void writeParcel() { p = Parcel.obtain(); byte b = 1; int i = 2; long l = 3; float f = 4; double d = 5; String s = "abcdefgh"; logWriteInfo("before writing"); p.writeByte(b); logWriteInfo("byte"); p.writeInt(i); logWriteInfo("int"); p.writeLong(l); logWriteInfo("long"); p.writeFloat(f); logWriteInfo("float"); p.writeDouble(d); logWriteInfo("double"); p.writeString(s); logWriteInfo("String"); } void logWriteInfo(String txt) { Log.d(LOG_TAG, txt + ": " + "dataSize = " + p.dataSize()); } void readParcel() { } void logReadInfo(String txt) { } }
Метод writeParcel – получаем экземпляр Parcel, описываем набор переменных и пишем их в Parcel, используя для этого соответствующие методы. После каждой записи выводим в лог информацию о Parcel, используя метод logWriteInfo.
Метод logWriteInfo пишет в лог данные о Parcel. dataSize – это объем записанных данных.
Методы readParcel и logReadInfo – пока пустые. Позже заполним.
Все сохраняем и запускаем приложение. Смотрим лог.
before writing: dataSize = 0
byte: dataSize = 4
int: dataSize = 8
long: dataSize = 16
float: dataSize = 20
double: dataSize = 28
String: dataSize = 52
Разбираем по порядку.
before writing: перед записью у нас размер данных равен 0. Записали byte: dataSize = 4 (для записи данных типа byte использовались 4 байта). Записали int: dataSize = 8 (для записи данных типа int использовались еще 4 байта в дополнение к ранее заполненным 4 байтам для byte). Записали long: dataSize = 16 (для записи long использовались еще 8 байтов в дополнение к ранее заполненным 8 байтам для byte и int). И т.д. В итоге видим, что dataSize показывает, сколько всего занято байт.
Обратите внимание, что типы int, long, float и double заняли столько байт, сколько они действительно занимают в Java – соответственно 4, 8, 4 и 8. byte – вместо одного байта почему-то занял целых 4. А String под каждый символ использует два байта, но пишет еще служебную информацию, поэтому получается больше.
Теперь попробуем прочесть то, что записали. Заполним пустые методы чтения:
void readParcel() { logReadInfo("before reading"); p.setDataPosition(0); logReadInfo("byte = " + p.readByte()); logReadInfo("int = " + p.readInt()); logReadInfo("long = " + p.readLong()); logReadInfo("float = " + p.readFloat()); logReadInfo("double = " + p.readDouble()); logReadInfo("string = " + p.readString()); } void logReadInfo(String txt) { Log.d(LOG_TAG, txt + ": " + "dataPosition = " + p.dataPosition()); }
В методе readParcel мы устанавливаем (метод setDataPosition) позицию в 0, т.к. нам нужно читать с начала. Читаем данные в том же порядке, как и записывали: byte, int, long, float, double, String. В лог выводим результат чтения и текущую позицию (dataPosition).
Все сохраним, запустим приложение и смотрим лог.
Первые строки лога про запись нам уже знакомы. Нас интересуют строки чтения.
before reading: dataPosition = 52
byte = 1: dataPosition = 4
int = 2: dataPosition = 8
long = 3: dataPosition = 16
float = 4.0: dataPosition = 20
double = 5.0: dataPosition = 28
string = abcdefgh: dataPosition = 52
Перед тем, как мы установим позицию в 0 (before reading), видим, что она равна 52. Там она находится после записи. Каждая запись данных перемещает позицию на кол-во, равное размеру записываемых данных. Размер всех последовательно записанных данных у нас составил 52, и позиция соответственно переместилась в 52. Вы можете в качестве эксперимента выводить в лог позицию после каждой записи данных. Я же вывожу только для процедур чтения.
Итак, мы устанавливаем позицию в 0 и начинаем читать данные. Прочли значение byte, оно равно 1, как мы и записывали. Позиция сместилась на размер прочтенного значения, и теперь мы будем читать с позиции 4. Читаем int, оно равно 2, позиция сместилась и равна 8. И т.д.
Все значения, которые мы последовательно записывали, мы в том же порядке считали. Здесь надо понимать, что если вы записали int, а читать потом будете double, то результат получится не тот, что нужен. Т.к. int пишет 4 байта, а double считывает 8. Тем самым он залезет на следующий записанный тип и возьмет из него недостающие 4 байта. Получится каша. Поэтому тут надо быть аккуратным.
Вы всегда можете установить нужную вам позицию и считать хранимое значение. Главное – знать, какой тип там хранится. Например, у нас сейчас при записи double пишется с позиции 20. Поэтому мы можем перевести позицию в 20 и выполнить readDouble. Мы успешно получим записанный туда double, а позиция станет равна 28.
Если вы хотите глянуть содержимое Parcel можно использовать его метод marshall(), он вернет массив записанных в Parcel байтов.
Вот такой краткий экскурс. Эти знания понадобятся для понимания следующего урока.
На следующем уроке:
- добавляем своему объекту поддержку Parcelable
- передаем объект с помощью Intent
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня