Как использовать посылку в Android?
Я пытаюсь использовать 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);
}
}
что я упустил?
обновление: чтобы упростить тест, я удалил чтение и запись файлов в моем исходном примере.
5 ответов
Ах, я, наконец, нашел проблему. На самом деле их было двое.
- создатель должен быть публичным, а не защищенным. Но что более важно,
- нужно позвонить
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-транспорт. Таким образом, нецелесообразно размещать какие-либо данные участка в постоянном хранилище: изменения в базовой реализации любых данных в участке могут отображать более старые данные нечитаемый.
от 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 принимает обработчик в своем конструкторе. Это называется от некоторых 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
класс, который посылки и 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
имеет решающее значение, как и результирующий конструктор, принимающий посылку. Я включил более сложные типы данных, чтобы вы могли см. раздел как посылать и unParcel массивы объектов Parcelable. Это обычная вещь при использовании Gson для преобразования JSON в объекты с детьми, как в этом примере.
чтобы лучше понять концепцию участка, попробуйте ссылку ниже
http://prasanta-paul.blogspot.com/2010/06/android-parcelable-example.html
надеюсь, что это помогает :)
У меня тоже была подобная проблема. только следующий фрагмент из emmby и этой помог мне.
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