Vantaggio dell'utilizzo di Parcelable invece di serializzare un oggetto


97

Da quanto ho capito, Bundlee Parcelableappartiene al modo in cui Android esegue la serializzazione. Viene utilizzato ad esempio nel passaggio di dati tra le attività. Ma mi chiedo, se ci sono vantaggi nell'usare al Parcelableposto della serializzazione classica in caso di salvataggio dello stato dei miei oggetti di business nella memoria interna per esempio? Sarà più semplice o più veloce del modo classico? Dove devo usare la serializzazione classica e dove è meglio usare i bundle?

Risposte:


99

Da "Pro Android 2"

NOTA: la visualizzazione di Parcelable potrebbe aver innescato la domanda, perché Android non utilizza il meccanismo di serializzazione Java integrato? Si scopre che il team di Android è giunto alla conclusione che la serializzazione in Java è troppo lenta per soddisfare i requisiti di comunicazione tra processi di Android. Quindi il team ha creato la soluzione Parcelable. L'approccio Parcelable richiede la serializzazione esplicita dei membri della classe, ma alla fine si ottiene una serializzazione molto più rapida dei propri oggetti.

Renditi conto anche che Android fornisce due meccanismi che ti consentono di passare i dati a un altro processo. Il primo è passare un bundle a un'attività utilizzando un intento e il secondo è passare un Parcelable a un servizio. Questi due meccanismi non sono intercambiabili e non devono essere confusi. Cioè, il Parcelable non deve essere passato a un'attività. Se vuoi iniziare un'attività e passarle dei dati, usa un bundle. Parcelable è pensato per essere utilizzato solo come parte di una definizione AIDL.


9
Che cos'è "Pro Android 2"?
AlikElzin-kilaka

79
Il secondo paragrafo non è vero, puoi passare un Parcelable come parametro a un'attività utilizzando il bundle ...
Ixx

3
Quando serializzo i miei oggetti creo un getBundlemetodo, quindi lo chiamo da writeToParcelas dest.writeBundle(getBundle());e ho entrambe le opzioni disponibili automaticamente nell'oggetto. Ci sono interessanti funzionalità di Parcel per oggetti live annotati qui: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: mi chiedevo perché fosse necessario passare un oggetto parcellizzabile tramite bundle all'attività. IMO, se lo fai, stai aggiungendo un ulteriore livello di serializzazione inutilmente e nient'altro.
Aumento

4
Philippe Breault ha scritto un bell'articolo su questo e ha anche aggiunto un test delle prestazioni. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

Serializableè comicamente lento su Android. Borderline inutile in molti casi insomma.

Parcele Parcelablesono incredibilmente veloci, ma la sua documentazione dice che non devi usarlo per la serializzazione generica nello storage, poiché l'implementazione varia con le diverse versioni di Android (cioè un aggiornamento del sistema operativo potrebbe rompere un'app che si basava su di esso).

La migliore soluzione per il problema della serializzazione dei dati nell'archiviazione a una velocità ragionevole è eseguire il rollio personale. Personalmente uso una delle mie classi di utilità che ha un'interfaccia simile a Parcele che può serializzare tutti i tipi standard in modo molto efficiente (a scapito della sicurezza dei tipi). Ecco una versione ridotta di esso:

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

}

2
Qual è la differenza tra l'utilizzo di questa tua classe personalizzata e l'implementazione dell'interfaccia Externalizable e il fare la stessa cosa?
Carrotman42

1
Il bundle serializza anche i nomi dei campi ... non è adatto per migliaia di oggetti.
Reuben Scratton,

1
Mi dispiace, inventare un altro serializzatore fa schifo - ora c'è solo un altro "Parcelable" da affrontare. Ce ne sono molte tra cui scegliere, con una libreria (la differenza è che la libreria è controllata, testata e utilizza un formato che altre persone usano): ProtocolBuffers, JSON, XML, ecc. È un peccato che la libreria Android faccia davvero schifo in questo senso .

2
Non credo che la frase di apertura sia più vera (5 anni dopo che è stata fatta). Uso la serializzazione Java senza problemi da molto tempo. Puoi trovare alcune cose potenzialmente interessanti su questo argomento in un post sul blog che ho appena scritto. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

1
Sì, non è più un problema del genere. Ho usato nient'altro che Serializable (con implementazioni readObject / writeObject ottimizzate) per diversi anni. In effetti, solo pochi giorni fa ho esaminato un dump esadecimale di alcuni oggetti serializzati e sono stato soddisfatto che non fosse troppo dispendioso.
Reuben Scratton


11

Se è necessaria la serializzazione, ad esempio per scopi di archiviazione, ma si desidera evitare la penalizzazione della velocità di riflessione sostenuta dall'interfaccia Serializable , è necessario creare esplicitamente il proprio protocollo di serializzazione con l' interfaccia Externalizable .

Se implementato correttamente, questo corrisponde alla velocità di Parcelable e tiene conto anche della compatibilità tra diverse versioni di Android e / o della piattaforma Java.

Anche questo articolo potrebbe chiarire le cose:

Qual è la differenza tra Serializable ed Externalizable in Java?

In una nota a margine, è anche la tecnica di serializzazione più veloce in molti benchmark, battendo Kryo, Avro, Protocol Buffers e Jackson (json):

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


7

Sembra che oggigiorno la differenza non sia così evidente, almeno non quando la esegui tra le tue attività.

Secondo i test mostrati su questo sito Web , Parcelable è circa 10 volte più veloce sui dispositivi più recenti (come il nexus 10) ed è circa 17 più veloce su quelli vecchi (come il desiderio Z)

quindi sta a te decidere se ne vale la pena.

forse per classi relativamente piccole e semplici, Serializable va bene, e per il resto, dovresti usare Parcelable


Penso che tu abbia ragione sui soldi dicendo che la differenza si sta riducendo. Tuttavia, ho scoperto che l'utilizzo di serializzabile può essere molto più efficiente in termini di dimensione dell'array di byte sottoposto a marshalling e che può aiutare a evitare TransactionTooLargeException. Mi piacerebbe sentire i vostri commenti su questo (mio) post del blog nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

Bene, potresti semplicemente mettere l'enorme oggetto che consuma memoria su una variabile statica e impostarlo su null proprio quando lo recuperi (su onCreate per esempio). Lo svantaggio è che non supporta i multi-processi ed è un modo un po 'sporco per farlo. Chissà se è così se desideri passare una bitmap di grandi dimensioni.
sviluppatore Android

4

Parcelable è principalmente correlato a IPC che utilizza l' infrastruttura Binder , in cui i dati vengono passati come pacchetti .

Poiché Android fa molto affidamento su Binder per la maggior parte, se non tutte, le attività IPC, ha senso implementare Parcelable nella maggior parte dei luoghi, e specialmente nel framework, perché consente di passare un oggetto a un altro processo se necessario. Rende gli oggetti "trasportabili".

Ma se si dispone di un livello aziendale non specifico per Android che utilizza ampiamente serializzabili per salvare gli stati degli oggetti e ha solo bisogno di memorizzarli nel file system, allora penso che serializzabile vada bene. Consente di evitare il codice della targa parcellizzabile.


In quali esempi vorresti memorizzare un oggetto reale nel file system saya? Perché non ottenere semplicemente il contenuto degli oggetti e memorizzare il contenuto effettivo in un file. Guarda JSON per esempio o anche xml. È possibile salvare oggetti in formato JSON o XML, oggetti come nei tipi POJO / Entity che costruiscono un tipico oggetto dati costruito principalmente con Stati, getter e setter per quello stato. In questo modo non è necessario serializzare gli oggetti allo scopo di memorizzarli poiché tutto ciò che ti interessa è lo stato degli oggetti
Jonathan


1

Uso semplicemente GSON -> Serializza su stringa JSON -> Ripristina oggetto da stringa JSON.


Questo va bene per piccoli oggetti, non tanto quando si hanno grandi array di oggetti con molte proprietà in ciascuno.
Nickmccomb

0

Inoltre Parcelable offre un'implementazione personalizzata in cui l'utente ha la possibilità di parcellizzare ciascuno dei suoi oggetti sovrascrivendo writeToParcel (), tuttavia la serializzazione non prevede questa implementazione personalizzata poiché il suo modo di passare i dati coinvolge l'API di riflessione JAVA.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.