Урок 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