59 Stimmen

Wie benutzt man Parcel in Android?

Ich versuche, die Parcel zu schreiben und dann zurückzulesen Parcelable . Wenn ich das Objekt aus der Datei zurücklese, kommt es aus irgendeinem Grund zurück als 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);
    }

}

Was übersehe ich?

UPDATE: Um den Test zu vereinfachen, habe ich das Lesen und Schreiben von Dateien in meinem ursprünglichen Beispiel entfernt.

75voto

emmby Punkte 98109

Ah, ich habe das Problem endlich gefunden. Es waren sogar zwei.

  1. CREATOR muss öffentlich sein, nicht geschützt. Aber noch wichtiger ist,
  2. Sie müssen anrufen setDataPosition(0) nachdem Sie Ihre Daten entschärft haben.

Hier ist der überarbeitete, funktionierende Code:

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);
    }

}

22voto

Carl D'Halluin Punkte 1022

Aufgepasst! Verwenden Sie Parcel nicht zur Serialisierung in eine Datei

Parcel ist kein universell einsetzbarer Serialisierungsmechanismus. Diese Klasse (und die zugehörige Parcelable-API, mit der beliebige Objekte in ein Parcel eingefügt werden können) ist als hochleistungsfähiger IPC-Transport konzipiert. Als solcher ist es nicht angebracht, Parcel-Daten in einen persistenten Speicher zu legen: Änderungen in der zugrundeliegenden Implementierung der Daten im Parcel können ältere Daten unlesbar machen.

von http://developer.Android.com/reference/Android/os/Parcel.html

15voto

David S. Punkte 6316

Ich habe festgestellt, dass Parcelable in Android am häufigsten innerhalb von Datenbündeln verwendet wird, insbesondere aber innerhalb eines Handlers, der Nachrichten sendet und empfängt. Als Beispiel könnte man ein AsyncTask oder eine Runnable die im Hintergrund laufen müssen, aber die resultierenden Daten an den Hauptthread senden oder Activity .

Hier ist ein einfaches Beispiel. Wenn ich eine Runnable das so aussieht:

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());
        }

    }
}

Wie Sie sehen können, nimmt diese Runnable in ihrem Konstruktor einen Handler auf. Dieser wird von einem Activity wie diese:

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();
    }

Nun bleibt nur noch der Kern der Frage, wie man eine Klasse als Parcelable . Ich habe eine ziemlich komplexe Klasse gewählt, um sie zu zeigen, weil es einige Dinge gibt, die man mit einer einfachen Klasse nicht sehen würde. Hier ist die ProductInfo Klasse, die Parcels und unParcels sauber:

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;
    }
}

Le site CREATOR ist kritisch, ebenso wie der daraus resultierende Konstruktor, der ein Parcel nimmt. Ich habe die komplexeren Datentypen einbezogen, damit Sie sehen können, wie man Arrays von Parcelable-Objekten parcelt und entparcelt. Dies ist eine übliche Sache, wenn man Gson verwendet, um JSON in Objekte mit Kindern zu konvertieren, wie in diesem Beispiel.

5voto

kAnNaN Punkte 3584

Um ein besseres Verständnis des Parzellenkonzepts zu erhalten, klicken Sie auf den folgenden Link

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

Ich hoffe, das hilft :)

1voto

A_rmas Punkte 734

Auch ich hatte ein ähnliches Problem. Nur das folgende Snippet aus emmbydiese hat mir geholfen.

    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();
        }

Sie sollte in jeder der Klassen, die Parcelable implementieren, enthalten sein

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X