Урок 68. Немного о Parcel
В этом уроке:
- знакомимся с 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