Korzyści z używania Parcelable zamiast serializacji obiektu

97

Jak rozumiem Bundlei Parcelablenależy do sposobu, w jaki Android wykonuje serializację w. Jest używany na przykład w przekazywaniu danych między działaniami. Ale zastanawiam się, czy są jakieś korzyści z używania Parcelablezamiast klasycznej serializacji, na przykład w przypadku zapisywania stanu moich obiektów biznesowych do pamięci wewnętrznej? Czy będzie to prostsze czy szybsze niż klasyczny sposób? Gdzie należy używać klasycznej serializacji, a gdzie lepiej używać pakietów?

Vladimir Ivanov
źródło

Odpowiedzi:

99

Z „Pro Android 2”

UWAGA: Widzenie Parcelable mogło wywołać pytanie, dlaczego Android nie używa wbudowanego mechanizmu serializacji Java? Okazuje się, że zespół Androida doszedł do wniosku, że serializacja w Javie jest zbyt wolna, aby spełnić wymagania komunikacji międzyprocesowej systemu Android. Dlatego zespół stworzył rozwiązanie Parcelable. Podejście Parcelable wymaga, abyś jawnie serializował członków swojej klasy, ale ostatecznie uzyskasz znacznie szybszą serializację swoich obiektów.

Pamiętaj również, że Android zapewnia dwa mechanizmy, które pozwalają na przekazywanie danych do innego procesu. Pierwszym jest przekazanie paczki do działania za pomocą intencji, a drugim przekazanie paczki do usługi. Te dwa mechanizmy nie są zamienne i nie należy ich mylić. Oznacza to, że Parcelable nie jest przeznaczone do przekazania do działania. Jeśli chcesz rozpocząć działanie i przekazać mu trochę danych, użyj pakietu. Parcelable ma być używane tylko jako część definicji AIDL.

rajath
źródło
9
Co to jest „Pro Android 2”?
AlikElzin-kilaka
79
Drugi akapit nie jest prawdziwy, możesz przekazać Parcelable jako parametr do czynności za pomocą pakietu ...
Ixx
3
Kiedy serializuję swoje obiekty, tworzę getBundlemetodę, a następnie wywołuję ją z writeToParcelas dest.writeBundle(getBundle());i mam obie opcje dostępne w obiekcie automatycznie. Tutaj odnotowano interesujące funkcje Parcel dla obiektów na żywo: developer.android.com/reference/android/os/Parcel.html
mikebabcock
2
@lxx: Zastanawiałem się, dlaczego należałoby przekazać obiekt do paczkowania przez pakiet do ćwiczenia. IMO, jeśli to zrobisz, niepotrzebnie dodasz jeszcze jeden poziom serializacji i nic więcej.
Wzrost
4
Philippe Breault napisał o tym fajny artykuł, a także dodał test wydajności. developerphil.com/parcelable-vs-serializable
WonderCsabo
23

Serializabledziała komicznie wolno na Androidzie. W rzeczywistości w wielu przypadkach jest to bezużyteczne.

Parceli Parcelablesą fantastycznie szybkie, ale ich dokumentacja mówi, że nie wolno ich używać do serializacji ogólnego przeznaczenia do pamięci masowej, ponieważ implementacja różni się w różnych wersjach Androida (tj. aktualizacja systemu operacyjnego może zepsuć aplikację, która na niej polegała).

Najlepszym rozwiązaniem problemu serializacji danych do pamięci z rozsądną szybkością jest przeniesienie własnej. Osobiście używam jednej z moich własnych klas narzędziowych, która ma podobny interfejs Parceli może bardzo efektywnie serializować wszystkie standardowe typy (kosztem bezpieczeństwa typów). Oto skrócona wersja:

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
źródło
2
Jaka jest różnica między używaniem tej własnej klasy a po prostu implementacją interfejsu eksternalizowalnego i robieniem tego samego?
Carrotman42
1
Pakiet serializuje również nazwy pól ... nie jest odpowiedni dla tysięcy obiektów.
Reuben Scratton,
1
Przepraszam, wymyślenie kolejnego serializatora jest do niczego - teraz jest jeszcze jeden „Parcelable” do załatwienia. Jest z czego wybierać, z biblioteką (różnica polega na tym, że biblioteka jest sprawdzona, przetestowana i używa formatu używanego przez inne osoby): ProtocolBuffers, JSON, XML itp. Szkoda, że ​​biblioteka Androida naprawdę jest do bani pod tym względem .
2
Nie sądzę, aby zdanie początkowe było już prawdziwe (5 lat po jego wydaniu). Od dłuższego czasu używam serializacji Java bez żadnych problemów. Możesz znaleźć kilka potencjalnie interesujących rzeczy na ten temat we wpisie na blogu, który właśnie napisałem. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
1
Tak, to już nie jest taki problem. Od kilku lat używam tylko Serializable (ze zoptymalizowanymi implementacjami readObject / writeObject). W rzeczywistości spojrzałem na zrzut heksadecymalny kilku zserializowanych obiektów zaledwie kilka dni temu i byłem zadowolony, że nie było to zbyt marnotrawne.
Reuben Scratton,
11

Jeśli potrzebujesz serializacji, np. Do celów przechowywania, ale chcesz uniknąć kary za szybkość refleksji wynikającej z interfejsu Serializable , powinieneś jawnie utworzyć własny protokół serializacji z interfejsem Externalizable .

Po prawidłowym wdrożeniu odpowiada to szybkości Parcelable, a także zapewnia kompatybilność między różnymi wersjami Androida i / lub platformy Java.

Ten artykuł może również wyjaśnić sprawę:

Jaka jest różnica między Serializable a Externalizable w Javie?

Na marginesie jest to również najszybsza technika serializacji w wielu testach porównawczych, pokonując Kryo, Avro, Protocol Buffers i Jacksona (json):

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

białobrązowy
źródło
7

Wydaje się, że w dzisiejszych czasach różnica nie jest tak zauważalna, przynajmniej nie wtedy, gdy prowadzisz ją między własnymi działaniami.

Zgodnie z testami pokazanymi na tej stronie , Parcelable jest około 10 razy szybszy na najnowszych urządzeniach (takich jak Nexus 10) i około 17 razy szybszy na starych (jak pragnienie Z)

więc to do Ciebie należy decyzja, czy warto.

może dla stosunkowo małych i prostych klas, Serializable jest w porządku, a dla reszty powinieneś użyć Parcelable

programista Androida
źródło
Myślę, że masz rację, mówiąc, że różnica się zmniejsza. Jednak odkryłem, że użycie serializowalnego może być znacznie bardziej wydajne pod względem rozmiaru zorganizowanej tablicy bajtów i może pomóc uniknąć TransactionTooLargeException. Chciałbym usłyszeć wasze komentarze na temat tego (mojego) wpisu na blogu nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
Cóż, możesz po prostu umieścić ogromny obiekt pochłaniający pamięć na zmiennej statycznej i ustawić ją na wartość null zaraz po jej pobraniu (na przykład onCreate). Wadą jest to, że nie obsługuje wielu procesów i jest to trochę brudny sposób. Zastanawiam się, czy tak jest, jeśli chcesz przekazać dużą mapę bitową.
programista Androida
4

Parcelable jest głównie związane z IPC korzystającym z infrastruktury Binder , gdzie dane są przekazywane jako Paczki .

Ponieważ Android w dużej mierze opiera się na Binderze dla większości, jeśli nie wszystkich, zadań IPC, sensowne jest wdrożenie Parcelable w większości miejsc, a zwłaszcza we frameworku, ponieważ pozwala on przekazać obiekt do innego procesu, jeśli tego potrzebujesz. Czyni obiekty „przenośnymi”.

Ale jeśli masz warstwę biznesową, która nie jest specyficzna dla Androida, która szeroko wykorzystuje elementy serializowalne do zapisywania stanów obiektów i potrzebujesz tylko przechowywać je w systemie plików, to myślę, że serializowalny jest w porządku. Pozwala to na uniknięcie kodu kotła Parcelable.

olivierg
źródło
W jakich przykładach chciałbyś przechowywać rzeczywisty obiekt w systemie plików saya? Dlaczego po prostu nie pobrać zawartości obiektów i zapisać rzeczywistą zawartość w pliku. Spójrz na przykład na JSON lub nawet XML. Możesz zapisywać obiekty w formacie JSON lub XML, obiekty tak jak w typach POJO / Entity, które konstruują typowy obiekt danych zbudowany głównie z Stany oraz metody pobierające i ustawiające dla tego stanu. W ten sposób nie ma potrzeby serializacji obiektów w celu ich przechowywania, ponieważ wszystko, na czym Ci zależy, to stan obiektów
Jonathan
1

Po prostu używam GSON -> Serialise to JSON String -> Restore Object from JSON String.

BLA
źródło
Jest to dobre dla małych obiektów, nie tak bardzo, gdy masz duże tablice obiektów z wieloma właściwościami w każdym.
Nickmccomb
0

Parcelable oferuje również niestandardową implementację, w której użytkownik ma szansę spakować każdy ze swoich obiektów przez nadpisanie metody writeToParcel (), jednak serializacja nie jest tą niestandardową implementacją, ponieważ jej sposób przekazywania danych obejmuje interfejs API odbicia JAVA.

Ajay Deepak
źródło