Nutzen Sie die Verwendung von Parcelable anstelle der Serialisierung von Objekten


97

Soweit ich weiß, gehört es zur Art Bundleund ParcelableWeise, wie Android die Serialisierung durchführt. Es wird beispielsweise zum Übertragen von Daten zwischen Aktivitäten verwendet. Ich frage mich jedoch, ob die Verwendung Parcelableanstelle der klassischen Serialisierung Vorteile bietet, wenn beispielsweise der Status meiner Geschäftsobjekte im internen Speicher gespeichert wird. Wird es einfacher oder schneller sein als der klassische Weg? Wo soll ich klassische Serialisierung verwenden und wo besser Bundles verwenden?

Antworten:


99

Von "Pro Android 2"

HINWEIS: Das Anzeigen von Parcelable hat möglicherweise die Frage ausgelöst, warum Android den integrierten Java-Serialisierungsmechanismus nicht verwendet. Es stellt sich heraus, dass das Android-Team zu dem Schluss gekommen ist, dass die Serialisierung in Java viel zu langsam ist, um die Interprozess-Kommunikationsanforderungen von Android zu erfüllen. Also baute das Team die Paketlösung. Der Parcelable-Ansatz erfordert, dass Sie die Mitglieder Ihrer Klasse explizit serialisieren. Am Ende erhalten Sie jedoch eine viel schnellere Serialisierung Ihrer Objekte.

Beachten Sie auch, dass Android zwei Mechanismen bietet, mit denen Sie Daten an einen anderen Prozess übergeben können. Die erste besteht darin, ein Bündel mit einer Absicht an eine Aktivität zu übergeben, und die zweite darin, ein Paket an einen Dienst zu übergeben. Diese beiden Mechanismen sind nicht austauschbar und sollten nicht verwechselt werden. Das heißt, das Paket soll nicht an eine Aktivität weitergegeben werden. Wenn Sie eine Aktivität starten und einige Daten übergeben möchten, verwenden Sie ein Bundle. Parcelable darf nur als Teil einer AIDL-Definition verwendet werden.


9
Was ist "Pro Android 2"?
AlikElzin-Kilaka

79
Der zweite Absatz ist nicht wahr, Sie können ein Paket als Parameter an eine Aktivität mit dem Bundle übergeben ...
Ixx

3
Wenn ich meine Objekte serialisiere, erstelle ich eine getBundleMethode und rufe diese von writeToParcelas auf. dest.writeBundle(getBundle());Beide Optionen stehen automatisch im Objekt zur Verfügung. Es gibt interessante
Paketfunktionen

2
@lxx: Ich habe mich gefragt, warum man ein paketierbares Objekt durch ein Bündel an die Aktivität übergeben muss. IMO, wenn Sie dies tun, fügen Sie unnötigerweise eine weitere Serialisierungsstufe hinzu und sonst nichts.
Steigen Sie

4
Philippe Breault hat einen schönen Artikel darüber verfasst und einen Leistungstest hinzugefügt. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

Serializableist komisch langsam auf Android. Borderline in vielen Fällen sogar nutzlos.

Parcelund Parcelablesind fantastisch schnell, aber seine Dokumentation besagt, dass Sie es nicht für die allgemeine Serialisierung zum Speicher verwenden dürfen, da die Implementierung je nach Android-Version unterschiedlich ist (dh ein Betriebssystem-Update kann eine App beschädigen, die darauf angewiesen ist).

Die beste Lösung für das Problem, Daten mit einer angemessenen Geschwindigkeit in den Speicher zu serialisieren, besteht darin, Ihre eigenen Daten zu rollen. Ich persönlich verwende eine meiner eigenen Utility-Klassen, die eine ähnliche Schnittstelle hat Parcelund alle Standardtypen sehr effizient serialisieren kann (auf Kosten der Typensicherheit). Hier ist eine gekürzte Version davon:

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
Was ist der Unterschied zwischen der Verwendung dieser benutzerdefinierten Klasse und der Implementierung der externisierbaren Schnittstelle und der gleichen Vorgehensweise?
Carrotman42

1
Bundle serialisiert auch die Feldnamen ... es ist nicht für Tausende von Objekten geeignet.
Reuben Scratton

1
Tut mir leid, die Erfindung eines weiteren Serialisierers ist zum Kotzen - jetzt gibt es nur noch ein weiteres "Paket", mit dem man sich befassen muss. Bei einer Bibliothek gibt es eine große Auswahl (der Unterschied besteht darin, dass die Bibliothek überprüft, getestet und in einem Format verwendet wird, das andere Benutzer verwenden): ProtocolBuffers, JSON, XML usw. Es ist eine Schande, dass die Android-Bibliothek in dieser Hinsicht wirklich scheiße ist .

2
Ich denke nicht, dass der Anfangssatz mehr wahr ist (5 Jahre nachdem er gemacht wurde). Ich verwende die Java-Serialisierung seit langer Zeit ohne Probleme. Sie können einige potenziell interessante Dinge zu diesem Thema in einem Blog-Beitrag finden, den ich gerade geschrieben habe. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

1
Ja, es ist nicht mehr wirklich so ein Problem. Ich habe mehrere Jahre lang nur Serializable (mit optimierten readObject / writeObject-Implementierungen) verwendet. Tatsächlich habe ich mir vor ein paar Tagen einen Hex-Dump mit ein paar serialisierten Objekten angesehen und war überzeugt, dass es nicht zu verschwenderisch war.
Reuben Scratton


11

Wenn Sie eine Serialisierung für Speicherzwecke benötigen, aber den Geschwindigkeitsverlust der Reflexion durch die serialisierbare Schnittstelle vermeiden möchten, sollten Sie explizit Ihr eigenes Serialisierungsprotokoll mit der externisierbaren Schnittstelle erstellen.

Bei ordnungsgemäßer Implementierung entspricht dies der Geschwindigkeit von Parcelable und berücksichtigt auch die Kompatibilität zwischen verschiedenen Versionen von Android und / oder der Java-Plattform.

Dieser Artikel könnte auch Dinge klären:

Was ist der Unterschied zwischen Serializable und Externalizable in Java?

Nebenbei bemerkt, es ist auch die schnellste Serialisierungstechnik in vielen Benchmarks und schlägt Kryo, Avro, Protocol Buffers und Jackson (json):

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


7

Es scheint, dass heutzutage der Unterschied nicht so auffällig ist, zumindest nicht, wenn Sie ihn zwischen Ihren eigenen Aktivitäten ausführen.

Laut den auf dieser Website gezeigten Tests ist Parcelable auf den neuesten Geräten (wie Nexus 10) etwa 10-mal schneller und auf alten Geräten (wie Wunsch Z) etwa 17-mal schneller.

Es liegt also an Ihnen, zu entscheiden, ob es sich lohnt.

Vielleicht ist Serializable für relativ kleine und einfache Klassen in Ordnung, und für den Rest sollten Sie Parcelable verwenden


Ich denke, Sie haben Recht mit dem Geld, wenn Sie sagen, dass der Unterschied abnimmt. Ich fand jedoch heraus, dass die Verwendung von serialisierbar in Bezug auf die Größe des gemarshallten Byte-Arrays viel effizienter sein kann und dazu beitragen kann, TransactionTooLargeException zu vermeiden. Ich würde gerne Ihre Kommentare zu diesem (meinem) Blog-Beitrag hören. Nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

Nun, Sie könnten einfach das riesige speicherintensive Objekt auf eine statische Variable setzen und es beim Abrufen auf null setzen (zum Beispiel bei onCreate). Der Nachteil ist, dass es keine Multi-Prozesse unterstützt und es ein bisschen schmutzig ist, dies zu tun. Ich frage mich, ob dies der Fall ist, wenn Sie eine große Bitmap übergeben möchten.
Android-Entwickler

4

Parcelable bezieht sich hauptsächlich auf IPC unter Verwendung der Binder- Infrastruktur, bei der Daten als Pakete übergeben werden .

Da Android für die meisten, wenn nicht alle IPC-Aufgaben stark auf Binder angewiesen ist, ist es sinnvoll, Parcelable an den meisten Stellen und insbesondere im Framework zu implementieren, da Sie bei Bedarf ein Objekt an einen anderen Prozess übergeben können. Es macht Objekte "transportabel".

Wenn Sie jedoch eine nicht Android-spezifische Geschäftsschicht haben, die häufig serialisierbare Dateien zum Speichern von Objektzuständen verwendet und diese nur im Dateisystem speichern muss, ist die serialisierbare Funktion meiner Meinung nach in Ordnung. Dadurch kann der Code für die Paketkesselplatte vermieden werden.


In welchen Beispielen möchten Sie ein tatsächliches Objekt im saya-Dateisystem speichern? Warum nicht einfach den Objektinhalt abrufen und den eigentlichen Inhalt in einer Datei speichern? Schauen Sie sich zum Beispiel JSON oder sogar XML an. Sie können Objekte im JSON- oder XML-Format speichern, Objekte wie in POJO / Entity-Typen, die ein typisches Datenobjekt erstellen, das hauptsächlich aus Status und Getter und Setter für diesen Status besteht. Auf diese Weise müssen Objekte nicht serialisiert werden, um sie zu speichern, da Sie sich nur um den
Jonathan,


1

Ich verwende nur GSON -> Serialisieren in JSON-Zeichenfolge -> Objekt aus JSON-Zeichenfolge wiederherstellen.


Dies ist in Ordnung für kleine Objekte, nicht so sehr, wenn Sie große Arrays von Objekten mit jeweils vielen Eigenschaften haben.
Nickmccomb

0

Parcelable bietet auch eine benutzerdefinierte Implementierung, bei der der Benutzer die Möglichkeit erhält, jedes seiner Objekte durch Überschreiben von writeToParcel () zu parzellieren. Bei der Serialisierung wird diese benutzerdefinierte Implementierung jedoch nicht verwendet, da die Datenübergabe eine JAVA-Reflektions-API umfasst.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.