Manfaat menggunakan Parcelable daripada membuat serialisasi objek

97

Seperti yang saya pahami, Bundledan Parcelabletermasuk cara Android melakukan serialisasi masuk Ini digunakan misalnya dalam melewatkan data di antara aktivitas. Tapi saya bertanya-tanya, apakah ada manfaat dalam menggunakan Parcelabledaripada serialisasi klasik dalam kasus menyimpan keadaan objek bisnis saya ke memori internal misalnya? Akankah lebih sederhana atau lebih cepat dari cara klasik? Di mana saya harus menggunakan serialisasi klasik dan di mana lebih baik menggunakan bundel?

Vladimir Ivanov
sumber

Jawaban:

99

Dari "Pro Android 2"

CATATAN: Melihat Parcelable mungkin telah memicu pertanyaan, mengapa Android tidak menggunakan mekanisme serialisasi Java bawaan? Ternyata tim Android sampai pada kesimpulan bahwa serialisasi di Java terlalu lambat untuk memenuhi persyaratan komunikasi antarproses Android. Jadi tim membuat solusi Parcelable. Pendekatan Parcelable mengharuskan Anda secara eksplisit membuat serial anggota kelas Anda, tetapi pada akhirnya, Anda mendapatkan serialisasi objek yang jauh lebih cepat.

Sadarilah juga bahwa Android menyediakan dua mekanisme yang memungkinkan Anda meneruskan data ke proses lain. Yang pertama adalah meneruskan bundel ke aktivitas menggunakan maksud, dan yang kedua adalah meneruskan Parcelable ke layanan. Kedua mekanisme ini tidak dapat dipertukarkan dan tidak boleh disalahartikan. Artinya, Parcelable tidak dimaksudkan untuk diteruskan ke suatu aktivitas. Jika Anda ingin memulai aktivitas dan meneruskan beberapa data, gunakan paket. Parcelable dimaksudkan untuk digunakan hanya sebagai bagian dari definisi AIDL.

rajath
sumber
9
Apa itu "Pro Android 2"?
AlikElzin-kilaka
79
Paragraf kedua tidak benar, Anda dapat meneruskan Parcelable sebagai parameter ke aktivitas menggunakan bundel ...
Ixx
3
Ketika saya membuat serial objek saya, saya membuat getBundlemetode, lalu memanggilnya dari writeToParcelas dest.writeBundle(getBundle());dan saya memiliki kedua opsi yang tersedia di objek secara otomatis. Ada fitur Parcel yang menarik untuk objek hidup yang dicatat di sini: developer.android.com/reference/android/os/Parcel.html
mikebabcock
2
@lxx: Saya bertanya-tanya mengapa seseorang perlu meneruskan objek yang dapat dipaketkan melalui bundel ke aktivitas. IMO jika Anda melakukannya, Anda tidak perlu menambahkan satu tingkat serialisasi lagi & tidak ada yang lain.
Naik
4
Philippe Breault membuat artikel yang bagus tentang ini, dan juga menambahkan tes kinerja. developerphil.com/parcelable-vs-serializable
WonderCsabo
23

Serializablesangat lambat di Android. Pada kenyataannya, garis batas tidak berguna dalam banyak kasus.

Parceldan Parcelablesangat cepat, tetapi dokumentasinya mengatakan Anda tidak boleh menggunakannya untuk serialisasi tujuan umum ke penyimpanan, karena penerapannya bervariasi dengan versi Android yang berbeda (misalnya, pembaruan OS dapat merusak aplikasi yang mengandalkannya).

Solusi terbaik untuk masalah serialisasi data ke penyimpanan dengan kecepatan yang wajar adalah menggulung sendiri. Saya pribadi menggunakan salah satu kelas utilitas saya sendiri yang memiliki antarmuka yang mirip Parceldan yang dapat membuat serialisasi semua tipe standar dengan sangat efisien (dengan mengorbankan keamanan tipe). Berikut adalah versi ringkasannya:

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++;
        }
    }

}
Ruben Scratton
sumber
2
Apa perbedaan antara menggunakan kelas kustom Anda ini dan hanya mengimplementasikan antarmuka Externalizable dan melakukan hal yang sama?
Carrotman42
1
Bundel juga membuat serial nama field ... itu tidak cocok untuk ribuan objek.
Ruben Scratton
1
Maaf, menemukan serializer lain menyebalkan - sekarang hanya ada "Parcelable" yang harus ditangani. Ada banyak pilihan, dengan pustaka (perbedaannya adalah pustaka diperiksa, diuji, dan menggunakan format yang digunakan orang lain): ProtocolBuffers, JSON, XML, dll. Sayang sekali pustaka Android benar-benar payah dalam hal ini .
2
Menurut saya kalimat pembuka itu tidak benar lagi (5 tahun setelah dibuat). Saya telah menggunakan serialisasi java tanpa masalah untuk waktu yang lama sekarang. Anda dapat menemukan beberapa hal yang berpotensi menarik tentang topik ini dalam postingan blog yang baru saja saya tulis. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
1
Ya, sebenarnya bukan masalah seperti itu lagi. Saya telah menggunakan tidak lebih dari Serializable (dengan implementasi readObject / writeObject yang dioptimalkan) selama beberapa tahun. Bahkan saya melihat hex dump dari beberapa objek serial hanya beberapa hari yang lalu dan merasa puas itu tidak terlalu boros.
Ruben Scratton
11

Jika Anda membutuhkan serialisasi untuk keperluan penyimpanan tetapi ingin menghindari penalti kecepatan refleksi yang disebabkan oleh antarmuka Serializable Anda harus secara eksplisit membuat protokol serialisasi Anda sendiri dengan antarmuka Eksternalizable .

Ketika diterapkan dengan benar, ini cocok dengan kecepatan Parcelable dan juga memperhitungkan kompatibilitas antara berbagai versi Android dan / atau platform Java.

Artikel ini mungkin juga menjelaskan semuanya:

Apa perbedaan antara Serializable dan Externalizable di Java?

Pada catatan samping, ini juga merupakan teknik serialisasi tercepat dalam banyak tolok ukur, mengalahkan Kryo, Avro, Protocol Buffer dan Jackson (json):

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

whitebrow
sumber
7

Tampaknya saat ini perbedaannya tidak begitu mencolok, setidaknya tidak ketika Anda menjalankannya di antara aktivitas Anda sendiri.

Menurut tes yang ditampilkan di situs web ini , Parcelable sekitar 10 kali lebih cepat pada perangkat terbaru (seperti nexus 10), dan 17 lebih cepat pada perangkat lama (seperti keinginan Z)

jadi terserah Anda untuk memutuskan apakah itu sepadan.

mungkin untuk kelas yang relatif kecil dan sederhana, Serializable baik-baik saja, dan selebihnya, Anda harus menggunakan Parcelable

pengembang android
sumber
Saya pikir Anda benar dengan mengatakan perbedaannya menyusut. Namun saya menemukan bahwa menggunakan serializable bisa jauh lebih efisien dalam hal ukuran array byte marshaled dan dapat membantu menghindari TransactionTooLargeException. Saya ingin mendengar komentar Anda tentang entri blog (milik saya) ini nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
Nah, Anda bisa meletakkan objek konsumsi memori yang besar pada variabel statis, dan menyetelnya ke null tepat saat Anda mengambilnya (misalnya di onCreate). Kerugiannya adalah tidak mendukung multi-proses, dan ini cara yang agak kotor untuk melakukannya. Bertanya-tanya apakah itu masalahnya jika Anda ingin meneruskan bitmap besar.
Pengembang android
4

Parcelable terutama terkait dengan IPC menggunakan infrastruktur Binder , di mana data diteruskan sebagai Paket .

Karena Android sangat bergantung pada Binder untuk sebagian besar, jika tidak semua, tugas IPC, masuk akal untuk mengimplementasikan Parcelable di sebagian besar tempat, dan terutama di framework, karena memungkinkan untuk meneruskan objek ke proses lain jika Anda membutuhkannya. Itu membuat objek "bisa diangkut".

Tetapi jika Anda memiliki lapisan bisnis non-Android khusus yang secara ekstensif menggunakan serializable untuk menyimpan status objek, dan hanya perlu menyimpannya ke sistem file, maka saya pikir serializable itu baik-baik saja. Ini memungkinkan untuk menghindari kode pelat boiler Parcelable.

olivierg.dll
sumber
Dalam contoh apa Anda ingin menyimpan objek aktual di sistem file saya? Mengapa tidak hanya mendapatkan konten objek dan menyimpan konten sebenarnya dalam sebuah file. Lihat JSON misalnya atau bahkan xml. Anda dapat menyimpan objek dalam format JSON atau XML, objek seperti dalam jenis POJO / Entitas yang membuat objek data tipikal yang dibangun dengan sebagian besar Status dan pengambil serta penyetel untuk status tersebut. Dengan cara itu tidak perlu membuat serial objek untuk tujuan menyimpannya karena yang Anda pedulikan adalah status objek
Jonathan
1

Berdasarkan artikel ini http://www.mooproductions.org/node/6?page=5 Parcelable seharusnya lebih cepat.

Tidak disebutkan dalam artikel, adalah bahwa saya tidak berpikir bahwa objek serializable akan bekerja di AIDL untuk layanan jarak jauh.

Mike dg
sumber
1

Saya hanya menggunakan GSON -> Serialise to JSON String -> Restore Object dari JSON String.

FOO
sumber
Ini bagus untuk objek kecil, tidak terlalu banyak ketika Anda memiliki array besar objek dengan banyak properti di masing-masing.
Nickmccomb
0

Parcelable juga menawarkan implementasi kustom di mana pengguna mendapat kesempatan untuk membagi setiap objeknya dengan menimpa writeToParcel (), Namun serialisasi tidak menerapkan kustomisasi ini karena caranya meneruskan data melibatkan API refleksi JAVA.

Ajay Deepak
sumber