Преимущество использования Parcelable вместо сериализации объекта
Насколько я понимаю, Bundle
а также Parcelable
принадлежит тому, как Android выполняет сериализацию. Он используется, например, при передаче данных между действиями. Но мне интересно, есть ли какие-либо преимущества в использовании Parcelable
вместо классической сериализации в случае сохранения состояния моих бизнес-объектов, например, во внутреннюю память? Будет ли это проще или быстрее классического способа? Где я должен использовать классическую сериализацию и где лучше использовать связки?
9 ответов
Из "Про Android 2"
ПРИМЕЧАНИЕ. Видение Parcelable могло вызвать вопрос: почему Android не использует встроенный механизм сериализации Java? Оказывается, команда Android пришла к выводу, что сериализация в Java слишком медленная, чтобы удовлетворить требования Android к межпроцессному взаимодействию. Поэтому команда создала решение Parcelable. Подход Parcelable требует явной сериализации членов вашего класса, но в конце вы получаете намного более быструю сериализацию ваших объектов.
Также следует понимать, что Android предоставляет два механизма, которые позволяют передавать данные другому процессу. Первый - передать пакет в действие с помощью намерения, а второй - передать Parcelable в службу. Эти два механизма не являются взаимозаменяемыми и их не следует путать. То есть Parcelable не предназначен для передачи в действие. Если вы хотите начать действие и передать ему некоторые данные, используйте пакет. Parcelable предназначен для использования только как часть определения AIDL.
Serializable
комично медленно на Android. Граница бесполезна во многих случаях на самом деле.
Parcel
а также Parcelable
фантастически быстрые, но в документации говорится, что вы не должны использовать их для сериализации общего назначения в хранилище, поскольку реализация зависит от версии Android (например, обновление ОС может привести к поломке приложения, на которое она опирается).
Наилучшее решение проблемы сериализации данных в хранилище с разумной скоростью - это сделать это самостоятельно. Я лично использую один из моих собственных служебных классов, который имеет аналогичный интерфейс Parcel
и который может очень эффективно сериализовать все стандартные типы (за счет безопасности типов). Вот сокращенная версия этого:
public interface Packageable {
public void readFromPackage(PackageInputStream in) throws IOException ;
public void writeToPackage(PackageOutputStream out) throws IOException ;
}
public final class PackageInputStream {
private DataInputStream input;
public PackageInputStream(InputStream in) {
input = new DataInputStream(new BufferedInputStream(in));
}
public void close() throws IOException {
if (input != null) {
input.close();
input = null;
}
}
// Primitives
public final int readInt() throws IOException {
return input.readInt();
}
public final long readLong() throws IOException {
return input.readLong();
}
public final long[] readLongArray() throws IOException {
int c = input.readInt();
if (c == -1) {
return null;
}
long[] a = new long[c];
for (int i=0 ; i<c ; i++) {
a[i] = input.readLong();
}
return a;
}
...
public final String readString() throws IOException {
return input.readUTF();
}
public final <T extends Packageable> ArrayList<T> readPackageableList(Class<T> clazz) throws IOException {
int N = readInt();
if (N == -1) {
return null;
}
ArrayList<T> list = new ArrayList<T>();
while (N>0) {
try {
T item = (T) clazz.newInstance();
item.readFromPackage(this);
list.add(item);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
N--;
}
return list;
}
}
public final class PackageOutputStream {
private DataOutputStream output;
public PackageOutputStream(OutputStream out) {
output = new DataOutputStream(new BufferedOutputStream(out));
}
public void close() throws IOException {
if (output != null) {
output.close();
output = null;
}
}
// Primitives
public final void writeInt(int val) throws IOException {
output.writeInt(val);
}
public final void writeLong(long val) throws IOException {
output.writeLong(val);
}
public final void writeLongArray(long[] val) throws IOException {
if (val == null) {
writeInt(-1);
return;
}
writeInt(val.length);
for (int i=0 ; i<val.length ; i++) {
output.writeLong(val[i]);
}
}
public final void writeFloat(float val) throws IOException {
output.writeFloat(val);
}
public final void writeDouble(double val) throws IOException {
output.writeDouble(val);
}
public final void writeString(String val) throws IOException {
if (val == null) {
output.writeUTF("");
return;
}
output.writeUTF(val);
}
public final <T extends Packageable> void writePackageableList(ArrayList<T> val) throws IOException {
if (val == null) {
writeInt(-1);
return;
}
int N = val.size();
int i=0;
writeInt(N);
while (i < N) {
Packageable item = val.get(i);
item.writeToPackage(this);
i++;
}
}
}
Если вам нужна сериализация, например, для целей хранения, но вы хотите избежать потери скорости отражения, вызванной интерфейсом Serializable, вы должны явно создать свой собственный протокол сериализации с интерфейсом Externalizable.
При правильной реализации это соответствует скорости Parcelable, а также учитывает совместимость между различными версиями Android и / или платформы Java.
Эта статья также может прояснить ситуацию:
В чем разница между Serializable и Externalizable в Java?
С другой стороны, это также самая быстрая техника сериализации во многих тестах, превосходящая Kryo, Avro, Protocol Buffers и Jackson (json):
http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
Кажется, что в настоящее время разница не так заметна, по крайней мере, когда вы работаете между своими действиями.
Согласно тестам, показанным на этом сайте, Parcelable примерно в 10 раз быстрее на новейших устройствах (например, nexus 10) и примерно на 17 быстрее на старых (например, desire Z).
так что вам решать, стоит ли это того.
может быть, для относительно небольших и простых классов Serializable - это хорошо, а для остальных вы должны использовать Parcelable
Parcelable в основном связан с IPC, использующим инфраструктуру Binder, где данные передаются как Parcels.
Поскольку Android во многом, если не во всех задачах IPC, во многом зависит от Binder, имеет смысл реализовать Parcelable в большинстве мест, особенно в среде, потому что он позволяет передавать объект другому процессу, если вам это нужно. Это делает объекты "транспортабельными".
Но если у вас есть бизнес-уровень, не относящийся к Android, который широко использует сериализуемые для сохранения состояний объектов, и вам нужно только сохранить их в файловой системе, то я думаю, что сериализуемый - это хорошо. Это позволяет избежать кода Parcelable.
Я просто использую GSON -> Serialise to JSON String -> Восстановить объект из JSON String.
На основании этой статьи http://www.mooproductions.org/node/6?page=5 Parcelable должен быть быстрее.
В статье не упоминается, что я не думаю, что сериализуемые объекты будут работать в AIDL для удаленных сервисов.
Кроме того, Parcelable предлагает пользовательскую реализацию, в которой пользователь получает возможность разбирать каждый из своих объектов, переопределяя writeToParcel(). Однако сериализация не делает эту пользовательскую реализацию, поскольку ее способ передачи данных включает API отражения JAVA.