オブジェクトをシリアル化する代わりにParcelableを使用する利点


97

私が理解し、通りBundleParcelableアンドロイドを行う連載で道に属している。それは、アクティビティ間のデータの受け渡しの例に使用されます。しかしParcelable、たとえば、ビジネスオブジェクトの状態を内部メモリに保存する場合に、従来のシリアル化の代わりに使用することで何か利点があるのでしょうか。従来の方法よりも簡単ですか、それとも高速ですか?クラシックシリアライゼーションをどこで使用する必要がありますか?

回答:


99

「プロAndroid 2」より

注:Parcelableを表示すると問題が発生した可能性がありますが、Androidが組み込みのJavaシリアル化メカニズムを使用しないのはなぜですか?Androidチームは、Javaでのシリアル化はAndroidのプロセス間通信要件を満たすには遅すぎるという結論に達したことがわかりました。そこで、チームはParcelableソリューションを構築しました。Parcelableアプローチでは、クラスのメンバーを明示的にシリアル化する必要がありますが、最終的にはオブジェクトのシリアル化がはるかに高速になります。

また、Androidには、データを別のプロセスに渡すことができる2つのメカニズムが用意されていることにも注意してください。1つ目は、インテントを使用してバンドルをアクティビティに渡し、2つ目はParcelableをサービスに渡します。これら2つのメカニズムは互換性がないため、混同しないでください。つまり、Parcelableはアクティビティに渡されることを意図していません。アクティビティを開始してデータを渡す場合は、バンドルを使用します。Parcelableは、AIDL定義の一部としてのみ使用されることを意図しています。


9
「プロAndroid 2」とは何ですか?
AlikElzin-kilaka 2012

79
2番目の段落は正しくありません。バンドルを使用してParcelableをパラメーターとしてアクティビティに渡すことができます...
Ixx

3
オブジェクトをシリアル化するときにgetBundleメソッドを作成し、それをwriteToParcelas から呼び出すとdest.writeBundle(getBundle());、オブジェクトで両方のオプションが自動的に使用可能になります。ここに記載されているライブオブジェクトには、興味深いParcel機能があります:developer.android.com/reference/android/os/Parcel.html
mikebabcock

2
@lxx:なぜバンドル可能なオブジェクトをバンドルを介してアクティビティに渡す必要があるのか​​と思っていました。IMOを実行すると、必要以上に1レベルのシリアル化が追加され、他には何も追加されません。
上昇

4
Philippe Breaultはこれについて素晴らしい記事を作成し、パフォーマンステストも追加しました。developerphil.com/parcelable-vs-serializable
WonderCsabo 2013

23

SerializableAndroidではコミカルに遅いです。境界線は実際には多くの場合役に立たない。

ParcelそしてParcelable迅速な幻想的ですが、そのドキュメントは、実装をAndroidのバージョンによって異なるため、ストレージへの汎用シリアル化に使用してはならないことを示しています(つまり、OSの更新により、それに依存するアプリが壊れる可能性があります)。

妥当な速度でデータをストレージにシリアル化するという問題の最良の解決策は、独自にロールすることです。私は個人的に、自分のユーティリティクラスの1つを使用します。これには、同様のインターフェイスが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 2012年

1
Bundleはフィールド名もシリアル化します...何千ものオブジェクトには適していません。
Reuben Scratton 2012

1
申し訳ありませんが、また別のシリアライザを発明することは吸う -今まだに対処するためのもう一つの「Parcelable」があります。ライブラリには、豊富な選択肢があります(ライブラリが吟味され、テストされ、他の人々が使用する形式を使用している点が異なります):ProtocolBuffers、JSON、XMLなど。これは、Androidライブラリがこの点で本当にうんざりしているのは残念です。 。

2
冒頭の文章はもう真実ではないと思います(作成後5年)。私は長い間問題なくJavaシリアライゼーションを使用してきました。このトピックに関する潜在的に興味深いことは、私が書いたばかりのブログ投稿で見つけることができます。nemanjakovacevic.net/blog/english/2015/03/24/...
ネマニャKovacevic

1
うん、それはもう本当にそのような問題ではありません。私は数年間、(最適化されたreadObject / writeObject実装で)Serializable以外に何も使用していません。実際、私はほんの数日前にいくつかのシリアル化されたオブジェクトの16進ダンプを見て、それはあまりにも無駄ではないと満足しました。
Reuben Scratton 2015年


11

ストレージなどの目的でシリアライゼーションが必要であるが、Serializableインターフェースによって生じるリフレクションの速度低下を避けたい場合は、Externalizableインターフェースを使用して独自のシリアライゼーションプロトコルを明示的に作成する必要があります。

適切に実装されている場合、これはParcelableの速度に一致し、AndroidやJavaプラットフォームの異なるバージョン間の互換性も考慮されます。

この記事は同様に事を片付けるかもしれません:

JavaのSerializableとExternalizableの違いは何ですか?

余談ですが、Kryo、Avro、Protocol Buffers、Jackson(json)を破って、多くのベンチマークで最速のシリアライゼーションテクニックでもあります。

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


7

最近では違いはそれほど目立たないようです。少なくとも自分のアクティビティ間で実行した場合はそうではありません。

このウェブサイトに示されているテストによると、Parcelableは最新のデバイス(Nexus 10など)で約10倍高速であり、古いデバイス(Desire Zなど)で約17高速です

それが価値があるかどうかを決めるのはあなた次第です。

多分比較的小さくシンプルなクラスの場合、Serializableで十分です。それ以外の場合は、Parcelableを使用する必要があります。


差は縮まっていると言って、あなたはその通りだと思います。しかし、serializableを使用すると、マーシャリングされたバイト配列のサイズの点ではるかに効率的になり、TransactionTooLargeExceptionを回避できることがわかりました。この(私の)ブログ投稿nemanjakovacevic.net/blog/english/2015/03/24/…–
Nemanja Kovacevicの

まあ、メモリを消費する巨大なオブジェクトを静的変数に置き、それをフェッチするときに(たとえばonCreateで)nullに設定するだけです。欠点は、マルチプロセスをサポートしていないことであり、それを行うにはちょっと汚い方法です。大きなビットマップを渡したい場合はそうでしょうか。
Android開発者

4

Parcelableは主に、データがParcelsとして渡されるBinderインフラストラクチャを使用するIPCに関連しています。

Androidは、すべてではないにしても、ほとんどのIPCタスクのほとんどをBinderに依存しているため、Parcelableをほとんどの場所、特にフレームワークに実装することは理にかなっています。オブジェクトを「移動可能」にします。

しかし、オブジェクトの状態を保存するためにシリアル化を広範囲に使用し、それらをファイルシステムに格納するだけでよいAndroid固有ではないビジネスレイヤーがある場合、シリアル化は問題ないと思います。Parcelableボイラープレートコードを回避できます。


どのような例で、sayaファイルシステムに実際のオブジェクトを保存しますか?オブジェクトのコンテンツを取得して、実際のコンテンツをファイルに保存しないでください。JSONの例やxmlを見てください。オブジェクトをJSONまたはXML形式で保存できます。オブジェクトは、主に状態とその状態のゲッターとセッターで構成される一般的なデータオブジェクトを構成するPOJO / Entityタイプのように保存できます。シリアル化する必要がないことな方法は、あなたが気にすべてとしてそれらを格納するためにオブジェクトに関するオブジェクトの状態がある
ジョナサン

1

この記事に基づくと、http: //www.mooproductions.org/node/6 page = 5 Parcelableの方が高速です。

記事には触れられていませんが、シリアル化可能なオブジェクトがリモートサービスのAIDLで機能することは考えていません。


1

GSON-> Serialize to JSON String-> Restore Object from JSON Stringを使用するだけです。


これは小さなオブジェクトには問題ありませんが、それぞれに多くのプロパティを持つオブジェクトの大きな配列がある場合はそれほどではありません。
Nickmccomb

0

また、Parcelableは、ユーザーがwriteToParcel()をオーバーライドして各オブジェクトをパーセルする機会を得るカスタム実装を提供します。ただし、シリアル化では、データを渡す方法にJAVAリフレクションAPIが含まれるため、このカスタム実装はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.