2009-10-26 9 views
50

Ich versuche Parcel zu verwenden, um eine Parcelable zu schreiben und dann zurückzulesen. Aus irgendeinem Grund, wenn ich das Objekt wieder aus der Datei lese, kommt es als null zurück.Wie verwende ich Parcel in Android?

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 fehlt mir?

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

Antwort

62

Ah, ich habe endlich das Problem gefunden. Es gab zwei tatsächlich.

  1. ERSTELLER muss öffentlich sein, nicht geschützt. Aber noch wichtiger:
  2. Sie müssen setDataPosition(0) nach dem Unmarshalling Ihrer Daten aufrufen.

Hier ist die überarbeitete, Arbeitscode:

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


} 
+3

implementiert Sie sparte mein Tag nur. VIELEN DANK. – Matthias

+0

Versuchen Sie das nicht mit Bitmaps in Ihrem Paket (in der Lage) :( –

+0

setDataPosition (0) gespeichert mich! Danke;) –

20

Vorsicht! Verwenden Sie kein Paket für die Serialisierung zu einer Datei.

Paket ist kein Allzweck-Serialisierungsmechanismus. Diese Klasse (und die entsprechende Parcelable-API zum Platzieren beliebiger Objekte in einem Paket) ist als Hochleistungs-IPC-Transport ausgelegt. Daher ist es nicht sinnvoll, Parcel-Daten in den persistenten Speicher zu legen: Änderungen in der zugrunde liegenden Implementierung der Daten im Parcel können ältere Daten unlesbar machen.

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

+14

Wie ist das hilfreich? – skaffman

+1

Können Sie einen alternativen Serialisierungsmechanismus vorschlagen? – aaronsnoswell

+2

@ aaronsnoswell Ich empfehle die Verwendung von Kryo oder die Implementierung der Java Externalizable-Schnittstelle (wenn Ihr Objekt nicht komplex ist) – nobre

12

finde ich, dass Parcelable wird am häufigsten in Android innerhalb von Daten Bu verwendet und zwar innerhalb eines Handlers, der Nachrichten sendet und empfängt. Als Beispiel haben Sie möglicherweise eine AsyncTask oder eine Runnable, die im Hintergrund ausgeführt werden muss, aber die resultierenden Daten an den Hauptthread oder Activity.

Hier ist ein einfaches Beispiel. Wenn ich einen Runnable haben, die wie folgt 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 dieser runnable einen Handler in seinem Konstruktor. Dies wird von einigen Activity wie folgt aufgerufen:

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

Jetzt ist alles, was übrig bleibt, ist das Herz dieser Frage, wie man eine Klasse als Parcelable definieren. Ich habe eine ziemlich komplexe Klasse ausgewählt, um sie zu zeigen, weil es einige Dinge gibt, die man nicht mit einem einfachen 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; 
    } 
} 

Die CREATOR kritisch ist, da der resultierende Konstruktor ist ein Paket nehmen. Ich habe die komplexeren Datentypen einbezogen, damit Sie sehen können, wie Sie Parcel-Objekte parzellieren und entpacken. Dies ist bei der Verwendung von Gson zum Konvertieren von JSON in Objekte mit untergeordneten Elementen wie in diesem Beispiel üblich.

1

Ich hatte auch ähnliches Problem. nur das folgende schnipsel von emmby und this 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(); 
     } 

Es sollte in jedem der Klasse gehalten werden, die Parcel