Beneficio de usar Parcelable en lugar de serializar objetos

97

Según tengo entendido, Bundley Parcelablepertenece a la forma en que Android realiza la serialización. Se usa, por ejemplo, para pasar datos entre actividades. Pero me pregunto, si hay algún beneficio en usar en Parcelablelugar de la serialización clásica en caso de guardar el estado de mis objetos comerciales en la memoria interna, por ejemplo. ¿Será más simple o más rápido que la forma clásica? ¿Dónde debo usar la serialización clásica y dónde es mejor usar paquetes?

Vladimir Ivanov
fuente

Respuestas:

99

Desde "Pro Android 2"

NOTA: Ver Parcelable podría haber provocado la pregunta, ¿por qué Android no utiliza el mecanismo de serialización de Java incorporado? Resulta que el equipo de Android llegó a la conclusión de que la serialización en Java es demasiado lenta para satisfacer los requisitos de comunicación entre procesos de Android. Entonces, el equipo construyó la solución Parcelable. El enfoque Parcelable requiere que serialices explícitamente los miembros de tu clase, pero al final, obtienes una serialización mucho más rápida de tus objetos.

También tenga en cuenta que Android proporciona dos mecanismos que le permiten pasar datos a otro proceso. El primero es pasar un paquete a una actividad usando una intención, y el segundo es pasar un Parcelable a un servicio. Estos dos mecanismos no son intercambiables y no deben confundirse. Es decir, el Parcelable no está destinado a pasarse a una actividad. Si desea iniciar una actividad y pasarle algunos datos, use un paquete. Parcelable está diseñado para usarse solo como parte de una definición AIDL.

Rajath
fuente
9
¿Qué es "Pro Android 2"?
AlikElzin-kilaka
79
El segundo párrafo no es cierto, puede pasar un Parcelable como parámetro a una actividad usando el paquete ...
Ixx
3
Cuando serializo mis objetos, creo un getBundlemétodo, luego lo llamo desde writeToParcelas dest.writeBundle(getBundle());y tengo ambas opciones disponibles en el objeto automáticamente. Hay características interesantes de Parcel para objetos en vivo que se indican aquí: developer.android.com/reference/android/os/Parcel.html
mikebabcock
2
@lxx: Me preguntaba por qué habría que pasar un objeto parcelable a través de un paquete a la actividad. En mi opinión, si lo hace, está agregando un nivel más de serialización innecesariamente y nada más.
Subida el
4
Philippe Breault hizo un buen artículo sobre esto y también agregó una prueba de rendimiento. developerphil.com/parcelable-vs-serializable
WonderCsabo
23

Serializablees cómicamente lento en Android. Límite inútil en muchos casos de hecho.

Parcely Parcelableson increíblemente rápidos, pero su documentación dice que no debe usarlo para la serialización de propósito general en el almacenamiento, ya que la implementación varía con las diferentes versiones de Android (es decir, una actualización del sistema operativo podría romper una aplicación que dependía de él).

La mejor solución para el problema de serializar los datos para almacenarlos a una velocidad razonable es rodar los suyos. Yo personalmente uso una de mis propias clases de utilidad que tiene una interfaz similar Parcely que puede serializar todos los tipos estándar de manera muy eficiente (a expensas de la seguridad de tipos). Aquí hay una versión resumida:

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++;
        }
    }

}
Reuben Scratton
fuente
2
¿Cuál es la diferencia entre usar esta clase personalizada suya y simplemente implementar la interfaz Externalizable y hacer lo mismo?
Carrotman42
1
Bundle también serializa los nombres de campo ... no es adecuado para miles de objetos.
Reuben Scratton
1
Lo siento, inventar otro serializador apesta , ahora hay otro "Parcelable" con el que lidiar. Hay mucho para elegir, con una biblioteca (la diferencia es que la biblioteca está examinada, probada y usa un formato que otras personas usan): ProtocolBuffers, JSON, XML, etc. Es una pena que la biblioteca de Android realmente apesta en este sentido. .
2
No creo que la oración inicial ya sea cierta (5 años después de que se hizo). He estado usando la serialización de Java sin ningún problema durante mucho tiempo. Puede encontrar algunas cosas potencialmente interesantes sobre este tema en una publicación de blog que acabo de escribir. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
1
Sí, ya no es un problema. No he usado nada más que Serializable (con implementaciones optimizadas de readObject / writeObject) durante varios años. De hecho, miré un volcado hexadecimal de algunos objetos serializados hace solo unos días y me sentí satisfecho de que no era un desperdicio.
Reuben Scratton
11

Si necesita serialización, por ejemplo, con fines de almacenamiento, pero desea evitar la penalización de velocidad de reflexión incurrida por la interfaz serializable , debe crear explícitamente su propio protocolo de serialización con la interfaz Externalizable .

Cuando se implementa correctamente, esto coincide con la velocidad de Parcelable y también tiene en cuenta la compatibilidad entre diferentes versiones de Android y / o la plataforma Java.

Este artículo también podría aclarar las cosas:

¿Cuál es la diferencia entre serializable y externalizable en Java?

En una nota al margen, también es la técnica de serialización más rápida en muchos puntos de referencia, superando a Kryo, Avro, Protocol Buffers y Jackson (json):

http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

whitebrow
fuente
7

Parece que hoy en día la diferencia no es tan notoria, al menos no cuando lo ejecutas entre tus propias actividades.

Según las pruebas mostradas en este sitio web , Parcelable es aproximadamente 10 veces más rápido en los dispositivos más nuevos (como nexus 10) y es aproximadamente 17 más rápido en los antiguos (como desire Z)

por lo que depende de usted decidir si vale la pena.

tal vez para clases relativamente pequeñas y simples, Serializable está bien, y para el resto, debe usar Parcelable

desarrollador de Android
fuente
Creo que tiene razón al decir que la diferencia se está reduciendo. Sin embargo, descubrí que el uso de serializable puede ser mucho más eficiente en términos de tamaño de la matriz de bytes calculada y eso puede ayudar a evitar TransactionTooLargeException. Me encantaría escuchar sus comentarios sobre esta publicación de blog (mía) nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
Bueno, podría simplemente poner el enorme objeto que consume memoria en una variable estática y establecerlo en nulo justo cuando lo recupere (en onCreate, por ejemplo). La desventaja es que no admite multiprocesos y es una forma un poco sucia de hacerlo. Me pregunto si ese es el caso si desea pasar un mapa de bits grande.
desarrollador de Android
4

Parcelable se relaciona principalmente con IPC utilizando la infraestructura de Binder , donde los datos se pasan como Parcels .

Dado que Android depende mucho de Binder para la mayoría, si no todas, las tareas de IPC, tiene sentido implementar Parcelable en la mayoría de los lugares, y especialmente en el marco, porque permite pasar un objeto a otro proceso si lo necesita. Hace que los objetos sean "transportables".

Pero si tiene una capa empresarial no específica de Android que usa ampliamente serializables para guardar estados de objetos, y solo necesita almacenarlos en el sistema de archivos, entonces creo que serializable está bien. Permite evitar el código placa caldera Parcelable.

olivierg
fuente
¿En qué ejemplos le gustaría almacenar un objeto real en el sistema de archivos saya? ¿Por qué no simplemente obtener el contenido de los objetos y almacenar el contenido real en un archivo? Mire JSON por ejemplo o incluso xml. Puede guardar objetos en formato JSON o XML, objetos como tipos POJO / Entity que construyen un objeto de datos típico construido principalmente con estados y captadores y definidores para ese estado. De esa manera, no hay necesidad de serializar objetos con el fin de almacenarlos, ya que todo lo que le importa es el estado de los objetos
Jonathan
1

Basado en este artículo http://www.mooproductions.org/node/6?page=5 Parcelable debería ser más rápido.

Lo que no se menciona en el artículo es que no creo que los objetos serializables funcionen en AIDL para servicios remotos.

Mike dg
fuente
1

Solo uso GSON -> Serializar a JSON String -> Restaurar objeto desde JSON String.

FOO
fuente
Esto está bien para objetos pequeños, no tanto cuando tiene grandes conjuntos de objetos con muchas propiedades en cada uno.
Nickmccomb
0

También Parcelable ofrece una implementación personalizada en la que el usuario tiene la oportunidad de agrupar cada uno de sus objetos anulando writeToParcel (). Sin embargo, la serialización no incluye esta implementación personalizada ya que su forma de pasar datos implica la API de reflexión JAVA.

Ajay Deepak
fuente