Parcel
を使用して、Parcelable
を書き込んでから読み戻そうとしています。何らかの理由で、ファイルからオブジェクトを読み戻すと、null
として返されます。
public void testFoo() {
final Foo orig = new Foo("blah blah");
// Wrote orig to a parcel and then byte array
final Parcel p1 = Parcel.obtain();
p1.writeValue(orig);
final byte[] bytes = p1.marshall();
// Check to make sure that the byte array seems to contain a Parcelable
assertEquals(4, bytes[0]); // Parcel.VAL_PARCELABLE
// Unmarshall a Foo from that byte array
final Parcel p2 = Parcel.obtain();
p2.unmarshall(bytes, 0, bytes.length);
final Foo result = (Foo) p2.readValue(Foo.class.getClassLoader());
assertNotNull(result); // FAIL
assertEquals( orig.str, result.str );
}
protected static class Foo implements Parcelable {
protected static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel source) {
final Foo f = new Foo();
f.str = (String) source.readValue(Foo.class.getClassLoader());
return f;
}
public Foo[] newArray(int size) {
throw new UnsupportedOperationException();
}
};
public String str;
public Foo() {
}
public Foo( String s ) {
str = s;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int ignored) {
dest.writeValue(str);
}
}
私は何が欠けていますか?
PDATE:テストを簡単にするために、元の例ではファイルの読み取りと書き込みを削除しました。
ああ、ようやく問題が見つかりました。実際には2つありました。
setDataPosition(0)
を呼び出す必要があります。修正された作業コードは次のとおりです。
public void testFoo() {
final Foo orig = new Foo("blah blah");
final Parcel p1 = Parcel.obtain();
final Parcel p2 = Parcel.obtain();
final byte[] bytes;
final Foo result;
try {
p1.writeValue(orig);
bytes = p1.marshall();
// Check to make sure that the byte stream seems to contain a Parcelable
assertEquals(4, bytes[0]); // Parcel.VAL_PARCELABLE
p2.unmarshall(bytes, 0, bytes.length);
p2.setDataPosition(0);
result = (Foo) p2.readValue(Foo.class.getClassLoader());
} finally {
p1.recycle();
p2.recycle();
}
assertNotNull(result);
assertEquals( orig.str, result.str );
}
protected static class Foo implements Parcelable {
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel source) {
final Foo f = new Foo();
f.str = (String) source.readValue(Foo.class.getClassLoader());
return f;
}
public Foo[] newArray(int size) {
throw new UnsupportedOperationException();
}
};
public String str;
public Foo() {
}
public Foo( String s ) {
str = s;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int ignored) {
dest.writeValue(str);
}
}
気をつけて!ファイルへのシリアル化にParcelを使用しないでください
パーセルは、汎用のシリアル化メカニズムではありません。このクラス(および任意のオブジェクトをパーセルに配置するための対応するParcelable API)は、高性能IPC=トランスポートとして設計されています。したがって、パーセルデータを永続的に配置することは適切ではありませんストレージ:パーセル内のデータの基礎となる実装の変更により、古いデータが読み取れなくなる可能性があります。
from http://developer.Android.com/reference/Android/os/Parcel.html
Parcelableは、データバンドル内のAndroidで最も頻繁に使用されますが、より具体的には、メッセージを送受信するハンドラー内で使用されます。たとえば、AsyncTask
または、バックグラウンドで実行する必要があるが、結果のデータをメインスレッドまたはRunnable
にポストするActivity
。
以下に簡単な例を示します。次のようなRunnable
がある場合:
package com.example;
import Java.io.InputStream;
import Java.io.InputStreamReader;
import Java.net.HttpURLConnection;
import Java.net.URL;
import Android.os.Bundle;
import Android.os.Handler;
import Android.os.Message;
import Android.util.Log;
import com.example.data.ProductInfo;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.squareup.okhttp.OkHttpClient;
public class AsyncRunnableExample extends Thread {
public static final String KEY = "AsyncRunnableExample_MSG_KEY";
private static final String TAG = AsyncRunnableExample.class.getSimpleName();
private static final TypeToken<ProductInfo> PRODUCTINFO =
new TypeToken<ProductInfo>() {
};
private static final Gson GSON = new Gson();
private String productCode;
OkHttpClient client;
Handler handler;
public AsyncRunnableExample(Handler handler, String productCode)
{
this.handler = handler;
this.productCode = productCode;
client = new OkHttpClient();
}
@Override
public void run() {
String url = "http://someserver/api/" + productCode;
try
{
HttpURLConnection connection = client.open(new URL(url));
InputStream is = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
// Deserialize HTTP response to concrete type.
ProductInfo info = GSON.fromJson(isr, PRODUCTINFO.getType());
Message msg = new Message();
Bundle b = new Bundle();
b.putParcelable(KEY, info);
msg.setData(b);
handler.sendMessage(msg);
}
catch (Exception err)
{
Log.e(TAG, err.toString());
}
}
}
ご覧のとおり、このrunnableはコンストラクターでHandlerを取ります。これは、次のようないくつかのActivity
から呼び出されます。
static class MyInnerHandler extends Handler{
WeakReference<MainActivity> mActivity;
MyInnerHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity theActivity = mActivity.get();
ProductInfo info = (ProductInfo) msg.getData().getParcelable(AsyncRunnableExample.KEY);
// use the data from the Parcelable 'ProductInfo' class here
}
}
}
private MyInnerHandler myHandler = new MyInnerHandler(this);
@Override
public void onClick(View v) {
AsyncRunnableExample thread = new AsyncRunnableExample(myHandler, barcode.getText().toString());
thread.start();
}
さて、残っているのはこの質問の核心です。クラスをParcelable
として定義する方法です。単純なクラスでは見られないことがいくつかあるため、表示するのにかなり複雑なクラスを選択しました。以下はProductInfo
クラスで、ParcelsとunParcelsをきれいにしています:
public class ProductInfo implements Parcelable {
private String brand;
private Long id;
private String name;
private String description;
private String slug;
private String layout;
private String large_image_url;
private String render_image_url;
private String small_image_url;
private Double price;
private String public_url;
private ArrayList<ImageGroup> images;
private ArrayList<ProductInfo> related;
private Double saleprice;
private String sizes;
private String colours;
private String header;
private String footer;
private Long productcode;
// getters and setters omitted here
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeString(description);
dest.writeString(slug);
dest.writeString(layout);
dest.writeString(large_image_url);
dest.writeString(render_image_url);
dest.writeString(small_image_url);
dest.writeDouble(price);
dest.writeString(public_url);
dest.writeParcelableArray((ImageGroup[])images.toArray(), flags);
dest.writeParcelableArray((ProductInfo[])related.toArray(), flags);
dest.writeDouble(saleprice);
dest.writeString(sizes);
dest.writeString(colours);
dest.writeString(header);
dest.writeString(footer);
dest.writeLong(productcode);
}
public ProductInfo(Parcel in)
{
id = in.readLong();
name = in.readString();
description = in.readString();
slug = in.readString();
layout = in.readString();
large_image_url = in.readString();
render_image_url = in.readString();
small_image_url = in.readString();
price = in.readDouble();
public_url = in.readString();
images = in.readArrayList(ImageGroup.class.getClassLoader());
related = in.readArrayList(ProductInfo.class.getClassLoader());
saleprice = in.readDouble();
sizes = in.readString();
colours = in.readString();
header = in.readString();
footer = in.readString();
productcode = in.readLong();
}
public static final Parcelable.Creator<ProductInfo> CREATOR = new Parcelable.Creator<ProductInfo>() {
public ProductInfo createFromParcel(Parcel in) {
return new ProductInfo(in);
}
public ProductInfo[] newArray(int size) {
return new ProductInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
}
CREATOR
は重要であり、結果のコンストラクターはパーセルを取得します。より複雑なデータ型を含めたため、Parcelableオブジェクトの配列をパーセルおよびアンパーセルする方法を確認できます。これは、Gsonを使用してJSONをこの例のように子を持つオブジェクトに変換する場合の一般的なことです。
パーセルのコンセプトをよりよく理解するには、以下のリンクを試してください
http://prasanta-paul.blogspot.com/2010/06/Android-parcelable-example.html
お役に立てれば :)
私も同様の問題を抱えていました。 emmbyと this の次のスニペットのみが助けになりました。
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel source) {
final Foo f = new Foo();
f.str = (String) source.readValue(Foo.class.getClassLoader());
return f;
}
public Foo[] newArray(int size) {
throw new UnsupportedOperationException();
}
Parcelableを実装する各クラスに保持する必要があります