Как использовать Parcel в Android? - PullRequest
54 голосов
/ 26 октября 2009

Я пытаюсь использовать 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 ]

70 голосов
/ 05 ноября 2009

Ах, я наконец нашел проблему. На самом деле их было два.

  1. СОЗДАТЕЛЬ должен быть публичным, не защищенным. Но что более важно,
  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);
    }


}
20 голосов
/ 08 июня 2010

Осторожно! Не используйте Parcel для сериализации в файл

Посылка не является механизмом сериализации общего назначения. Этот класс (и соответствующий API Parcelable для размещения произвольных объектов в Parcel) разработан как высокопроизводительный транспорт IPC. В связи с этим нецелесообразно помещать какие-либо данные об участках в постоянное хранилище: изменения в базовой реализации любых данных из участка могут сделать более старые данные нечитаемыми.

из http://developer.android.com/reference/android/os/Parcel.html

15 голосов
/ 19 ноября 2013

Я считаю, что 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());
        }

    }
}

Как видите, этот исполняемый объект принимает в своем конструкторе обработчик. Это вызывается из некоторого 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, который выполняет чистую обработку и разбор посылок:

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 в объекты с дочерними элементами, как в этом примере.

5 голосов
/ 24 февраля 2011

Чтобы лучше понять концепцию посылки, попробуйте ссылку ниже

http://prasanta -paul.blogspot.com / 2010/06 / Android-parcelable-example.html

надеюсь, это поможет:)

1 голос
/ 11 декабря 2015

У меня тоже была похожая проблема. только следующий фрагмент из 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

...