私が理解し、通りBundle
やParcelable
アンドロイドを行う連載で道に属している。それは、アクティビティ間のデータの受け渡しの例に使用されます。しかしParcelable
、たとえば、ビジネスオブジェクトの状態を内部メモリに保存する場合に、従来のシリアル化の代わりに使用することで何か利点があるのでしょうか。従来の方法よりも簡単ですか、それとも高速ですか?クラシックシリアライゼーションをどこで使用する必要がありますか?
私が理解し、通りBundle
やParcelable
アンドロイドを行う連載で道に属している。それは、アクティビティ間のデータの受け渡しの例に使用されます。しかしParcelable
、たとえば、ビジネスオブジェクトの状態を内部メモリに保存する場合に、従来のシリアル化の代わりに使用することで何か利点があるのでしょうか。従来の方法よりも簡単ですか、それとも高速ですか?クラシックシリアライゼーションをどこで使用する必要がありますか?
回答:
「プロAndroid 2」より
注:Parcelableを表示すると問題が発生した可能性がありますが、Androidが組み込みのJavaシリアル化メカニズムを使用しないのはなぜですか?Androidチームは、Javaでのシリアル化はAndroidのプロセス間通信要件を満たすには遅すぎるという結論に達したことがわかりました。そこで、チームはParcelableソリューションを構築しました。Parcelableアプローチでは、クラスのメンバーを明示的にシリアル化する必要がありますが、最終的にはオブジェクトのシリアル化がはるかに高速になります。
また、Androidには、データを別のプロセスに渡すことができる2つのメカニズムが用意されていることにも注意してください。1つ目は、インテントを使用してバンドルをアクティビティに渡し、2つ目はParcelableをサービスに渡します。これら2つのメカニズムは互換性がないため、混同しないでください。つまり、Parcelableはアクティビティに渡されることを意図していません。アクティビティを開始してデータを渡す場合は、バンドルを使用します。Parcelableは、AIDL定義の一部としてのみ使用されることを意図しています。
getBundle
メソッドを作成し、それをwriteToParcel
as から呼び出すとdest.writeBundle(getBundle());
、オブジェクトで両方のオプションが自動的に使用可能になります。ここに記載されているライブオブジェクトには、興味深いParcel機能があります:developer.android.com/reference/android/os/Parcel.html
Serializable
Androidではコミカルに遅いです。境界線は実際には多くの場合役に立たない。
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++;
}
}
}
ストレージなどの目的でシリアライゼーションが必要であるが、Serializableインターフェースによって生じるリフレクションの速度低下を避けたい場合は、Externalizableインターフェースを使用して独自のシリアライゼーションプロトコルを明示的に作成する必要があります。
適切に実装されている場合、これはParcelableの速度に一致し、AndroidやJavaプラットフォームの異なるバージョン間の互換性も考慮されます。
この記事は同様に事を片付けるかもしれません:
JavaのSerializableとExternalizableの違いは何ですか?
余談ですが、Kryo、Avro、Protocol Buffers、Jackson(json)を破って、多くのベンチマークで最速のシリアライゼーションテクニックでもあります。
http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
最近では違いはそれほど目立たないようです。少なくとも自分のアクティビティ間で実行した場合はそうではありません。
このウェブサイトに示されているテストによると、Parcelableは最新のデバイス(Nexus 10など)で約10倍高速であり、古いデバイス(Desire Zなど)で約17高速です
それが価値があるかどうかを決めるのはあなた次第です。
多分比較的小さくシンプルなクラスの場合、Serializableで十分です。それ以外の場合は、Parcelableを使用する必要があります。
Parcelableは主に、データがParcelsとして渡されるBinderインフラストラクチャを使用するIPCに関連しています。
Androidは、すべてではないにしても、ほとんどのIPCタスクのほとんどをBinderに依存しているため、Parcelableをほとんどの場所、特にフレームワークに実装することは理にかなっています。オブジェクトを「移動可能」にします。
しかし、オブジェクトの状態を保存するためにシリアル化を広範囲に使用し、それらをファイルシステムに格納するだけでよいAndroid固有ではないビジネスレイヤーがある場合、シリアル化は問題ないと思います。Parcelableボイラープレートコードを回避できます。
GSON-> Serialize to JSON String-> Restore Object from JSON Stringを使用するだけです。
また、Parcelableは、ユーザーがwriteToParcel()をオーバーライドして各オブジェクトをパーセルする機会を得るカスタム実装を提供します。ただし、シリアル化では、データを渡す方法にJAVAリフレクションAPIが含まれるため、このカスタム実装はありません。