Перевага використання Parcelable замість об'єкта серіалізації


97

Як я розумію, Bundleі Parcelableналежить до того , як Android преформ сериализации. Він використовується, наприклад , в передачі даних між різними видами діяльності. Але мені цікаво, чи є якісь переваги у використанні Parcelableзамість класичної серіалізації у випадку збереження стану моїх бізнес-об’єктів у внутрішній пам'яті? Це буде простіше чи швидше, ніж класичний спосіб? Де я повинен використовувати класичну серіалізацію та де краще використовувати пачки?

Відповіді:


99

Від "Pro Android 2"

ПРИМІТКА. Якщо бачити Parcelable, можливо, це викликало питання, чому Android не використовує вбудований механізм серіалізації Java? Виявляється, команда Android дійшла висновку, що серіалізація на Java набагато надто повільна, щоб задовольнити вимоги міжпроцесорних комунікацій Android. Тож команда побудувала рішення Parcelable. Підхід Parcelable вимагає, щоб ви явно серіалізували членів свого класу, але, зрештою, ви отримаєте набагато швидшу серіалізацію своїх об'єктів.

Також усвідомлюйте, що Android надає два механізми, які дозволяють передавати дані в інший процес. Перший - передавати пакет до діяльності з використанням наміру, а другий - передавати Parcelable службі. Ці два механізми не є взаємозамінними і їх не слід плутати. Тобто, Парцелябел не призначений для передачі діяльності. Якщо ви хочете розпочати діяльність і передати їй деякі дані, використовуйте пакет. Парцелябельний призначений використовуватись лише як частина визначення AIDL.


9
Що таке "Pro Android 2"?
AlikElzin-kilaka

79
Другий абзац не відповідає дійсності. Ви можете передати Parcelable як параметр діяльності, використовуючи пакет ...
Ixx,

3
Коли я серіалізую свої об'єкти, я створюю getBundleметод, а потім викликаю це з writeToParcelяк dest.writeBundle(getBundle());і у мене є автоматично доступні обидва варіанти. Тут відзначені цікаві функції посилок для живих об’єктів: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: Мені було цікаво, чому потрібно передати об'єкт, що розширюється, через пакет, щоб перейти до діяльності. IMO, якщо ви це зробите, ви додаєте ще один рівень серіалізації без потреби та нічого іншого.
Підйом

4
Філіп Бро зробив приємну статтю з цього приводу, а також додав тест на працездатність. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

Serializableна Android ідеально повільно. Насправді прикордонна лінія марна у багатьох випадках.

Parcelі Parcelableвони фантастично швидкі, але в його документації написано, що ви не повинні використовувати його для серіалізації загального призначення для зберігання, оскільки реалізація залежить від різних версій Android (тобто оновлення ОС може зламати додаток, на який покладаються).

Найкраще рішення проблеми серіалізації даних для зберігання з розумною швидкістю - це зробити свій власний. Я особисто використовую один із власних класів утиліт, який має подібний інтерфейс Parcelі який може дуже ефективно серіалізувати всі типові типи (за рахунок безпеки типу). Ось скорочена версія його:

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
Яка різниця між використанням цього користувальницького вашого класу та просто реалізацією інтерфейсу Externalizable і тим же самим?
Carrotman42

1
Пакет також серіалізує назви полів ... він не підходить для тисяч об'єктів.
Рубін Скреттон

1
До жаль, придумуючи ще один серіалайзер відстій - тепер це просто ще один «Parcelable» , щоб мати справу з. Є з чого вибрати, з бібліотекою (різниця полягає в тому, що бібліотека перевірена, протестована та використовує формат, який використовують інші люди): ProtocolBuffers, JSON, XML тощо. Прикро, що бібліотека Android насправді в цьому відносить .

2
Я не думаю, що вступний вирок вже є істинним (5 років після його винесення). Я вже давно використовую серіалізацію Java без будь-яких проблем. Ви можете знайти деякі потенційно цікаві речі на цю тему у щоденнику блогу. nemanjakovacevic.net/blog/english/2015/03/24/…
Неманья Ковачевич

1
Так, це насправді вже не таке питання. Я кілька років не використовував нічого, крім Serializable (з оптимізованими реалізаціями readObject / writeObject). Насправді я подивився на шестигранний смітник декількох серіалізованих об’єктів лише кілька днів тому і був задоволений, що це не надто марно.
Рубін Скреттон


11

Якщо вам потрібна серіалізація для цілей зберігання, але ви хочете уникнути покарання швидкості відображення, понесеного інтерфейсом Serializable, вам слід явно створити власний протокол серіалізації за допомогою інтерфейсу Externalizable .

При правильній реалізації це відповідає швидкості Parcelable, а також пояснює сумісність між різними версіями Android та / або платформи Java.

Ця стаття може також прояснити речі:

Яка різниця між Serializable та Externalizable у Java?

З іншого боку, це також найшвидша техніка серіалізації в багатьох тестах, перемігши Крио, Авро, Буфер протоколу та Джексона (json):

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


7

Здається, що сьогодні різниця не настільки помітна, принаймні, не тоді, коли ви здійснюєте її між своєю власною діяльністю.

Відповідно до тестів, показаних на цьому веб-сайті , Parcelable на найновіших пристроях (наприклад, Nexus 10) приблизно в 10 разів швидший, а на старих - приблизно на 17 швидше (наприклад, бажання Z)

тож вирішувати, чи варто воно того

можливо, для відносно невеликих і простих занять серіалізація - це добре, а для решти слід використовувати Parcelable


Я думаю, ти маєш рацію на гроші, кажучи, що різниця зменшується. Однак я виявив, що використання серіалізатора може бути набагато ефективніше з точки зору розміру масиву байтованих байтів, що може допомогти уникнути TransactionTooLargeException. Я хотів би почути ваші коментарі до цього (мого) повідомлення в блозі nemanjakovacevic.net/blog/english/2015/03/24/…
Неманя Ковачевич

Ну, ви можете просто поставити величезний об'єкт, що споживає пам'ять, на статичну змінну і встановити його на нульове значення під час його отримання (наприклад, на onCreate). Недоліком є ​​те, що він не підтримує багатопроцесорні процеси, і це трохи брудно. Задумайтеся, чи це так, якщо ви хочете передати великий растровий малюнок.
андроїд розробник

4

Parcelable пов'язаний в основному з IPC з використанням інфраструктури Binder , де дані передаються у вигляді посилок .

Оскільки Android багато покладається на Binder для більшості, якщо не всіх завдань IPC, має сенс реалізувати Parcelable в більшості місць, особливо в рамках, тому що він дозволяє передати об'єкт в інший процес, якщо вам це потрібно. Це робить об’єкти "транспортабельними".

Але якщо у вас бізнес-шар, не специфічний для Android, який широко використовує серіалізатори для збереження станів об'єктів, і потрібно лише зберігати їх у файловій системі, то я вважаю, що серіалізація - це добре. Це дозволяє уникнути парцелябельного кодового котла.


У яких прикладах ви хочете зберегти фактичний об’єкт у файловій системі сайти? Чому б просто не отримати вміст об'єктів і зберегти фактичний вміст у файлі. Подивіться, наприклад, на JSON або навіть xml. Ви можете зберігати об'єкти у форматі JSON або XML, об'єкти, як у типах POJO / Entity, які будують типовий об'єкт даних, побудований здебільшого штатів, а також гетерів та сетерів для цього стану. Таким чином, немає потреби в серіалізації об'єктів з метою їх зберігання, оскільки все, що вам цікаво, - це стан об’єктів
Джонатан,

1

На основі цієї статті http://www.mooproductions.org/node/6?page=5 Парцелябел повинен бути швидшим.

Не згадується в статті, що я не припускаю, що серіалізаційні об'єкти будуть працювати в AIDL для віддалених служб.


1

Я просто використовую GSON -> Серіалізація до рядка JSON -> Відновлення об'єкта з JSON String.


Це добре для невеликих об’єктів, не так вже й багато, коли у вас є великі масиви об’єктів з великою кількістю властивостей у кожному.
Nickmccomb

0

Також Parcelable пропонує користувацьку реалізацію, коли користувач отримує шанс посилкувати кожен із своїх об'єктів, переосмислюючи writeToParcel (), однак серіалізація не забезпечує цю спеціальну реалізацію, оскільки спосіб передачі даних передбачає API відображення JAVA.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.