Преимущество использования Parcelable вместо сериализации объекта


97

Насколько я понимаю, Bundleи Parcelableотносится к способу сериализации в Android. Он используется, например, при передаче данных между действиями. Но мне интересно, есть ли какие-то преимущества в использовании Parcelableвместо классической сериализации, например, в случае сохранения состояния моих бизнес-объектов во внутреннюю память? Будет ли это проще или быстрее классического? Где использовать классическую сериализацию, а где лучше использовать пакеты?

Ответы:


99

Из "Pro Android 2"

ПРИМЕЧАНИЕ. Просмотр Parcelable мог вызвать вопрос, почему Android не использует встроенный механизм сериализации Java? Оказывается, команда Android пришла к выводу, что сериализация в Java слишком медленная, чтобы удовлетворить требования Android к межпроцессному взаимодействию. Итак, команда создала решение Parcelable. Подход Parcelable требует, чтобы вы явно сериализовали члены вашего класса, но в конечном итоге вы получите гораздо более быструю сериализацию ваших объектов.

Также помните, что Android предоставляет два механизма, которые позволяют передавать данные другому процессу. Первый - передать пакет активности с помощью намерения, а второй - передать Parcelable службе. Эти два механизма не взаимозаменяемы, и их не следует путать. То есть Parcelable не предназначен для передачи в действие. Если вы хотите запустить действие и передать ему какие-то данные, используйте пакет. Parcelable предназначен для использования только как часть определения AIDL.


9
Что такое «Pro Android 2»?
AlikElzin-kilaka 02

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

3
Когда я сериализую свои объекты, я создаю getBundleметод, затем вызываю его из writeToParcelas, dest.writeBundle(getBundle());и у меня автоматически доступны оба параметра в объекте. Здесь отмечены интересные особенности Parcel для живых объектов: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: мне было интересно, зачем нужно передавать пакетный объект через пакет в действие. ИМО, если вы это сделаете, вы добавляете еще один уровень сериализации без необходимости и ничего больше.
Rise

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
Bundle также сериализует имена полей ... он не подходит для тысяч объектов.
Рубен Скрэттон,

1
Извините, изобретать еще один сериализатор - отстой - теперь есть еще один "Parcelable", с которым нужно иметь дело. Есть из чего выбрать, есть библиотека (разница в том, что библиотека проверена, протестирована и использует формат, который используют другие люди): ProtocolBuffers, JSON, XML и т. Д. Жаль, что библиотека Android действительно отстой в этом отношении .

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

1
Да, это уже не такая уж проблема. Я не использовал ничего, кроме Serializable (с оптимизированными реализациями readObject / writeObject) в течение нескольких лет. Фактически, я просмотрел шестнадцатеричный дамп нескольких сериализованных объектов всего несколько дней назад и убедился, что это не слишком расточительно.
Reuben Scratton


11

Если вам нужна сериализация, например, для целей хранения, но вы хотите избежать потери скорости отражения, вызванной интерфейсом Serializable, вы должны явно создать свой собственный протокол сериализации с интерфейсом Externalizable .

При правильной реализации это соответствует скорости Parcelable, а также учитывает совместимость между различными версиями Android и / или платформы Java.

Эта статья также может прояснить ситуацию:

В чем разница между Serializable и Externalizable в Java?

Кстати, это также самый быстрый метод сериализации во многих тестах, превосходящий Kryo, Avro, Protocol Buffers и Jackson (json):

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


7

Кажется, что в настоящее время разница не так заметна, по крайней мере, когда вы запускаете ее между своими действиями.

Согласно тестам, показанным на этом веб-сайте , Parcelable примерно в 10 раз быстрее на новейших устройствах (например, Nexus 10) и примерно в 17 раз быстрее на старых (например, desire Z).

так что вам решать, стоит ли оно того.

возможно, для относительно небольших и простых классов Serializable подойдет, а для остальных вы должны использовать Parcelable


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

Что ж, вы можете просто поместить огромный объект, потребляющий память, в статическую переменную и установить для него значение null сразу после его извлечения (например, в onCreate). Недостатком является то, что он не поддерживает несколько процессов, и это немного грязный способ сделать это. Интересно, так ли это, если вы хотите передать большое растровое изображение.
разработчик Android,

4

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

Поскольку Android во многом полагается на Binder для большинства, если не для всех задач IPC, имеет смысл реализовать Parcelable в большинстве мест, особенно во фреймворке, потому что он позволяет передавать объект другому процессу, если вам это нужно. Это делает объекты «транспортабельными».

Но если у вас есть бизнес-уровень, не относящийся к Android, который широко использует сериализуемые объекты для сохранения состояний объекта и вам нужно только сохранять их в файловой системе, то я думаю, что сериализуемость - это нормально. Это позволяет избежать кода Parcelable Котельная.


В каких примерах вы хотели бы сохранить реальный объект в файловой системе saya? Почему бы просто не получить содержимое объектов и не сохранить фактическое содержимое в файле. Посмотрите, например, JSON или даже xml. Вы можете сохранять объекты в формате JSON или XML, объекты как в типах POJO / Entity, которые создают типичный объект данных, состоящий в основном из состояний, а также методов получения и установки для этого состояния. Таким образом, нет необходимости сериализовать объекты с целью их хранения, поскольку все, что вас волнует, это состояние объектов
Джонатан

1

Основываясь на этой статье http://www.mooproductions.org/node/6?page=5 Parcelable должен быть быстрее.

В статье не упоминается, что я не думаю, что сериализуемые объекты будут работать в AIDL для удаленных сервисов.


1

Я просто использую GSON -> Serialise to JSON String -> Restore Object from JSON String.


Это нормально для небольших объектов, не особенно, когда у вас есть большие массивы объектов с большим количеством свойств в каждом.
Nickmccomb

0

Также Parcelable предлагает настраиваемую реализацию, в которой пользователь получает возможность разделить каждый из своих объектов, переопределив writeToParcel (). Однако сериализация не поддерживает эту настраиваемую реализацию, поскольку ее способ передачи данных включает API отражения JAVA.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.