Avantage d'utiliser Parcelable au lieu de sérialiser l'objet


97

Si je comprends bien, Bundleet Parcelableappartient à la façon dont Android effectue la sérialisation. Il est utilisé par exemple pour transmettre des données entre les activités. Mais je me demande s'il y a des avantages à utiliser Parcelableau lieu de la sérialisation classique en cas de sauvegarde de l'état de mes objets métier dans la mémoire interne par exemple? Sera-ce plus simple ou plus rapide que la méthode classique? Où dois-je utiliser la sérialisation classique et où utiliser les bundles?

Réponses:


99

Depuis «Pro Android 2»

REMARQUE: voir Parcelable peut avoir déclenché la question, pourquoi Android n'utilise-t-il pas le mécanisme de sérialisation Java intégré? Il s'avère que l'équipe Android est parvenue à la conclusion que la sérialisation en Java est bien trop lente pour satisfaire les exigences de communication interprocessus d'Android. L'équipe a donc créé la solution Parcelable. L'approche Parcelable nécessite que vous sérialisiez explicitement les membres de votre classe, mais au final, vous obtenez une sérialisation beaucoup plus rapide de vos objets.

Sachez également qu'Android fournit deux mécanismes qui vous permettent de transmettre des données à un autre processus. La première consiste à transmettre un bundle à une activité à l'aide d'un intent, et la seconde à transmettre un Parcelable à un service. Ces deux mécanismes ne sont pas interchangeables et ne doivent pas être confondus. Autrement dit, le Parcelable n'est pas destiné à être transmis à une activité. Si vous souhaitez démarrer une activité et lui transmettre des données, utilisez un bundle. Parcelable est destiné à être utilisé uniquement dans le cadre d'une définition AIDL.


9
Qu'est-ce que "Pro Android 2"?
AlikElzin-kilaka

79
Le deuxième paragraphe n'est pas vrai, vous pouvez passer un Parcelable comme paramètre à une activité en utilisant le bundle ...
Ixx

3
Lorsque je sérialise mes objets, je crée une getBundleméthode, puis l'appelle depuis writeToParcelas dest.writeBundle(getBundle());et les deux options sont automatiquement disponibles dans l'objet. Il y a des fonctionnalités de parcelles intéressantes pour les objets vivants notées ici: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: Je me demandais pourquoi il faudrait passer un objet parcellable via bundle à l'activité. IMO si vous le faites, vous ajoutez un niveau supplémentaire de sérialisation inutilement et rien d'autre.
Augmentation du

4
Philippe Breault a fait un bel article à ce sujet, et a également ajouté un test de performance. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

Serializableest comiquement lent sur Android. Borderline inutile dans de nombreux cas en fait.

Parcelet Parcelablesont incroyablement rapides, mais sa documentation indique que vous ne devez pas l'utiliser pour une sérialisation générale vers le stockage, car l'implémentation varie selon les différentes versions d'Android (c'est-à-dire qu'une mise à jour du système d'exploitation pourrait casser une application qui en dépendait).

La meilleure solution pour le problème de la sérialisation des données vers le stockage à une vitesse raisonnable est de lancer la vôtre. J'utilise personnellement l'une de mes propres classes d'utilitaires qui a une interface similaire à Parcelet qui peut sérialiser tous les types standard très efficacement (au détriment de la sécurité des types). En voici une version abrégée:

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
Quelle est la différence entre utiliser votre classe personnalisée et implémenter simplement l'interface Externalizable et faire la même chose?
Carrotman42

1
Bundle sérialise également les noms de champs ... il ne convient pas à des milliers d'objets.
Reuben Scratton

1
Désolé, inventer encore un autre sérialiseur est nul - il n'y a maintenant qu'un autre "Parcelable" à gérer. Il y a beaucoup de choix, avec une bibliothèque (la différence est que la bibliothèque est vérifiée, testée et utilise un format que d'autres personnes utilisent): ProtocolBuffers, JSON, XML, etc. C'est dommage que la bibliothèque Android suce vraiment à cet égard .

2
Je ne pense pas que la phrase d'ouverture soit plus vraie (5 ans après qu'elle a été faite). J'utilise la sérialisation Java sans aucun problème depuis longtemps maintenant. Vous pouvez trouver des choses potentiellement intéressantes sur ce sujet dans un article de blog que je viens d'écrire. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

1
Oui, ce n'est plus vraiment un problème. Je n'ai utilisé que Serializable (avec des implémentations optimisées de readObject / writeObject) pendant plusieurs années. En fait, j'ai regardé un vidage hexadécimal de quelques objets sérialisés il y a seulement quelques jours et j'étais convaincu que ce n'était pas trop de gaspillage.
Reuben Scratton


11

Si vous avez besoin d'une sérialisation à des fins de stockage, mais que vous voulez éviter la pénalité de vitesse de réflexion encourue par l' interface Serializable , vous devez créer explicitement votre propre protocole de sérialisation avec l' interface Externalizable .

Lorsqu'il est correctement mis en œuvre, cela correspond à la vitesse de Parcelable et tient également compte de la compatibilité entre les différentes versions d'Android et / ou de la plate-forme Java.

Cet article pourrait également clarifier les choses:

Quelle est la différence entre Serializable et Externalizable en Java?

En passant, c'est aussi la technique de sérialisation la plus rapide dans de nombreux benchmarks, battant Kryo, Avro, Protocol Buffers et Jackson (json):

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


7

Il semble que de nos jours, la différence n'est pas si notable, du moins pas lorsque vous l'exécutez entre vos propres activités.

Selon les tests présentés sur ce site Web , Parcelable est environ 10 fois plus rapide sur les appareils les plus récents (comme le nexus 10) et environ 17 plus rapide sur les anciens (comme le desire Z)

c'est donc à vous de décider si cela en vaut la peine.

peut-être pour les classes relativement petites et simples, Serializable est bien, et pour le reste, vous devriez utiliser Parcelable


Je pense que vous avez raison sur l'argent en disant que la différence diminue. Cependant, j'ai découvert que l'utilisation de sérialisable peut être beaucoup plus efficace en termes de taille du tableau d'octets marshalé et cela peut aider à éviter TransactionTooLargeException. J'aimerais entendre vos commentaires sur ce (le mien) article de blog nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

Eh bien, vous pouvez simplement placer l'énorme objet consommant de la mémoire sur une variable statique, et le définir sur null dès que vous le récupérez (sur onCreate par exemple). L'inconvénient est qu'il ne prend pas en charge les multi-processus, et c'est une manière un peu sale de le faire. Je me demande si c'est le cas si vous souhaitez passer un grand bitmap.
développeur android

4

Parcelable est principalement lié à IPC en utilisant l' infrastructure Binder , où les données sont transmises sous forme de colis .

Étant donné qu'Android s'appuie beaucoup sur Binder pour la plupart, sinon la totalité, des tâches IPC, il est logique d'implémenter Parcelable dans la plupart des endroits, et en particulier dans le cadre, car cela permet de passer un objet à un autre processus si vous en avez besoin. Il rend les objets «transportables».

Mais si vous avez une couche métier non spécifique à Android qui utilise largement les sérialisables pour enregistrer les états des objets et n'a besoin de les stocker que dans le système de fichiers, alors je pense que la sérialisable est bien. Il permet d'éviter le code de la plaque chauffante colisable.


Dans quels exemples souhaiteriez-vous stocker un objet réel dans le système de fichiers saya? Pourquoi ne pas simplement obtenir le contenu des objets et stocker le contenu réel dans un fichier. Regardez JSON par exemple ou même xml. Vous pouvez enregistrer des objets au format JSON ou XML, des objets comme dans les types POJO / Entity qui construisent un objet de données typique construit avec principalement des états et des getters et des setters pour cet état. De cette façon, il n'est pas nécessaire de sérialiser les objets dans le but de les stocker car tout ce qui vous importe est l'état des objets
Jonathan


1

J'utilise juste GSON -> Serialise to JSON String -> Restore Object from JSON String.


C'est très bien pour les petits objets, pas tellement quand vous avez de grands tableaux d'objets avec beaucoup de propriétés dans chacun.
Nickmccomb

0

Parcelable propose également une implémentation personnalisée où l'utilisateur a la possibilité de parcelliser chacun de ses objets en remplaçant le writeToParcel (), mais la sérialisation ne permet pas cette implémentation personnalisée car sa manière de transmettre les données implique l'API de réflexion JAVA.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.