Nesneyi serileştirmek yerine Parcelable kullanmanın faydası


97

Anladığım kadarıyla Bundleve ParcelableAndroid'in serileştirmeyi gerçekleştirme şekline aittir. Örneğin aktiviteler arasında veri geçişinde kullanılır. Ama merak ediyorum, Parcelablemesela iş nesnelerimin durumunu dahili belleğe kaydetme durumunda klasik serileştirme yerine kullanmanın bir faydası var mı? Klasik yoldan daha basit mi yoksa daha hızlı mı olacak? Klasik serileştirmeyi nerede ve paketleri daha iyi nerede kullanmalıyım?

Yanıtlar:


99

"Pro Android 2" den

NOT: Parcelable'ı görmek soruyu tetiklemiş olabilir, Android neden yerleşik Java serileştirme mekanizmasını kullanmıyor? Android ekibinin, Java'daki serileştirmenin Android'in işlemler arası iletişim gereksinimlerini karşılamak için çok yavaş olduğu sonucuna vardıkları ortaya çıktı. Böylece ekip Parcelable çözümünü geliştirdi. Parcelable yaklaşımı, sınıfınızın üyelerini açıkça serileştirmenizi gerektirir, ancak sonunda, nesnelerinizin çok daha hızlı serileştirilmesini elde edersiniz.

Ayrıca, Android'in verileri başka bir işleme aktarmanıza izin veren iki mekanizma sağladığını da unutmayın. Birincisi, bir amaç kullanarak bir aktiviteye bir paket geçirmek, ikincisi ise bir Parselable'ı bir hizmete geçirmektir. Bu iki mekanizma birbirinin yerine kullanılamaz ve karıştırılmamalıdır. Yani Parselable'ın bir aktiviteye aktarılması amaçlanmamıştır. Bir aktivite başlatmak ve bazı verileri aktarmak istiyorsanız, bir paket kullanın. Parcelable, yalnızca AIDL tanımının bir parçası olarak kullanılmak içindir.


9
"Pro Android 2" nedir?
AlikElzin-kilaka

79
İkinci paragraf doğru değil, paketi kullanarak bir etkinliğe Parcelable'ı parametre olarak geçirebilirsiniz ...
Ixx

3
Ben oluşturmak benim nesneleri seri zaman getBundleyöntemini, o andan itibaren böyle hitap writeToParcelolarak dest.writeBundle(getBundle());ve ben otomatik nesne mevcut iki seçeneği vardır. Burada belirtilen canlı nesneler için ilginç Parcel özellikleri vardır: developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx: Neden parsele edilebilir bir nesneyi paket aracılığıyla etkinliğe iletmek gerektiğini merak ediyordum. IMO bunu yaparsanız, gereksiz yere bir serileştirme düzeyi daha eklemiş olursunuz ve başka bir şey eklememiş olursunuz.
yükseliş

4
Philippe Breault bununla ilgili güzel bir makale yazdı ve ayrıca bir performans testi ekledi. developerphil.com/parcelable-vs-serializable
WonderCsabo

23

SerializableAndroid'de komik bir şekilde yavaş. Aslında çoğu durumda sınırda işe yaramaz.

Parcelve Parcelablefevkalade hızlıdır, ancak dokümantasyonu , Android'in farklı sürümlerine göre uygulama değişiklik gösterdiğinden depolamaya genel amaçlı serileştirme için kullanmamanız gerektiğini söylüyor (yani bir işletim sistemi güncellemesi, ona güvenen bir uygulamayı bozabilir).

Verileri makul bir hızda depolamaya serileştirme sorunu için en iyi çözüm, kendi çözümünüzü almaktır. Ben şahsen, benzer bir arayüze sahip olan Parcelve tüm standart türleri çok verimli bir şekilde seri hale getirebilen (tip güvenliği pahasına) kendi yardımcı sınıflarımdan birini kullanıyorum . İşte bunun kısaltılmış bir versiyonu:

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
Bu özel sınıfınızı kullanmakla sadece Haricileştirilebilir arayüzü uygulamakla aynı şeyi yapmak arasındaki fark nedir?
Carrotman42

1
Paket, alan adlarını da seri hale getirir ... binlerce nesne için uygun değildir.
Reuben Scratton

1
Maalesef, başka bir serileştirici icat etmek berbat - şimdi uğraşılması gereken başka bir "Parcelable" var. Bir kitaplıktan seçim yapabileceğiniz çok şey var (aradaki fark, kitaplığın incelenmiş, test edilmiş olması ve diğer insanların kullandığı bir biçimi kullanmasıdır): ProtocolBuffers, JSON, XML, vb. Android kitaplığının bu açıdan gerçekten berbat olması utanç verici. .

2
Artık açılış cümlesinin doğru olduğunu düşünmüyorum (yapıldıktan 5 yıl sonra). Uzun zamandır herhangi bir sorun yaşamadan java serileştirme kullanıyorum. Az önce yazdığım bir blog yazısında bu konu hakkında potansiyel olarak ilginç şeyler bulabilirsiniz. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

1
Evet, artık gerçekten böyle bir sorun değil. Birkaç yıldır Serializable'dan (optimize edilmiş readObject / writeObject uygulamalarıyla) başka bir şey kullanmadım. Aslında sadece birkaç gün önce birkaç serileştirilmiş nesnenin onaltılık bir dökümüne baktım ve çok israf olmadığına ikna oldum.
Reuben Scratton


11

Eğer yani depolama amaçlı seri gerek ama uğradıkları yansıma hızı düşüşünden kaçınmak istiyorsanız Serializable'için açıkça kendi seri hale protokolünü oluşturmalısınız arayüzü Externalizable arayüzüne.

Düzgün uygulandığında, bu Parcelable'ın hızıyla eşleşir ve ayrıca Android ve / veya Java platformunun farklı sürümleri arasındaki uyumluluğu hesaba katar.

Bu makale de işleri netleştirebilir:

Java'da Serializable ve Externalizable arasındaki fark nedir?

Bir yandan da, birçok kıyaslamada en hızlı serileştirme tekniğidir, Kryo, Avro, Protocol Buffers ve Jackson'ı (json) yener:

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


7

Görünüşe göre bugünlerde fark o kadar da anlaşılmaz değil, en azından kendi faaliyetleriniz arasında çalıştırdığınızda.

Bu web sitesinde gösterilen testlere göre , Parcelable, en yeni cihazlarda (nexus 10 gibi) yaklaşık 10 kat daha hızlı ve eskilerde (arzu Z gibi) yaklaşık 17 kat daha hızlı.

bu yüzden buna değip değmeyeceğine karar vermek size kalmış.

belki nispeten küçük ve basit sınıflar için, Serializable iyidir ve geri kalanı için Parcelable'ı kullanmalısınız


Farkın azaldığını söyleyerek haklısın. Ancak, serileştirilebilir kullanmanın, sıralanan bayt dizisinin boyutu açısından çok daha verimli olabileceğini ve TransactionTooLargeException'dan kaçınmaya yardımcı olabileceğini öğrendim. Bu (benim) blog gönderisiyle ilgili yorumlarınızı duymak isterim nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

Eh, büyük bellek tüketen nesneyi statik bir değişkene koyabilir ve onu getirdiğinizde (örneğin onCreate üzerinde) boş olarak ayarlayabilirsiniz. Dezavantajı, çoklu süreçleri desteklememesi ve bunu yapmanın biraz kirli bir yolu olmasıdır. Büyük bir bitmap'i geçirmek istiyorsanız durumun bu olup olmadığını merak edin.
android geliştiricisi

4

Parcelable, verilerin Parseller olarak aktarıldığı Binder altyapısını kullanan IPC ile ilgilidir .

Android, IPC görevlerinin tümü olmasa da çoğu için Binder'a çok güvendiğinden, çoğu yerde ve özellikle çerçevede Parcelable'ı uygulamak mantıklıdır, çünkü buna ihtiyacınız olursa bir nesneyi başka bir işleme aktarmanıza izin verir. Nesneleri "taşınabilir" hale getirir.

Ancak, nesne durumlarını kaydetmek için yaygın olarak serileştirilebilirleri kullanan ve yalnızca dosya sisteminde depolamanız gereken, Android'e özgü olmayan bir iş katmanınız varsa, o zaman serileştirilebilirliğin iyi olduğunu düşünüyorum. Parcelable kazan plakası kodundan kaçınmaya izin verir.


Hangi örneklerde gerçek bir nesneyi saya dosya sisteminde saklamak istersiniz? Neden sadece nesnelerin içeriğini alıp gerçek içeriği bir dosyada depolamıyorsunuz? Örneğin JSON'a veya hatta xml'ye bakın. Nesneleri JSON veya XML biçiminde, POJO / Varlık türlerinde olduğu gibi, çoğunlukla Durumlar ve bu durum için alıcılar ve ayarlayıcılarla oluşturulmuş tipik bir veri nesnesi oluşturan nesneleri kaydedebilirsiniz. Bu şekilde, tüm ilgilendiğiniz nesnelerin durumu olduğundan, nesneleri depolamak amacıyla serileştirmenize gerek kalmaz
Jonathan


1

Sadece GSON -> JSON Dizgesine Seri Oluştur -> Nesneyi JSON Dizesinden Geri Yükle'yi kullanıyorum.


Bu, küçük nesneler için uygundur, her birinde çok sayıda özelliğe sahip büyük nesne dizileriniz olduğunda pek de geçerli değildir.
Nickmccomb

0

Ayrıca Parcelable, kullanıcının writeToParcel () 'i geçersiz kılarak nesnelerinin her birini paketleyebilme şansı elde ettiği özel bir uygulama sunar. Ancak, veri iletme yolu JAVA yansıma API'sini içerdiğinden serileştirme bu özel uygulamayı yapmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.