web-dev-qa-db-fra.com

Comment utiliser Parcel dans Android?

J'essaie d'utiliser Parcel pour écrire puis relire un Parcelable. Pour une raison quelconque, lorsque je relis l'objet à partir du fichier, il revient sous la forme 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);
    }


}

Qu'est-ce que je rate?

PDATE: Pour simplifier le test, j'ai supprimé la lecture et l'écriture des fichiers dans mon exemple d'origine.

54
emmby

Ah, j'ai finalement trouvé le problème. Il y en avait deux en fait.

  1. LE CRÉATEUR doit être public et non protégé. Mais plus important,
  2. Vous devez appeler setDataPosition(0) après avoir démasqué vos données.

Voici le code de travail révisé:

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


}
70
emmby

Il faut se méfier! N'utilisez pas Parcel pour la sérialisation dans un fichier

Le colis n'est pas un mécanisme de sérialisation à usage général. Cette classe (et l'API Parcelable correspondante pour placer des objets arbitraires dans une parcelle) est conçue comme un transport haute performance IPC. En tant que tel, il n'est pas approprié de placer des données de parcelle dans des données persistantes stockage: les modifications de l'implémentation sous-jacente de l'une des données du colis peuvent rendre les données plus anciennes illisibles.

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

20
Carl D'Halluin

Je trouve que Parcelable est le plus souvent utilisé dans Android dans les ensembles de données, mais plus spécifiquement dans un gestionnaire qui envoie et reçoit des messages. Par exemple, vous pourriez avoir un AsyncTask ou un Runnable qui doit s'exécuter en arrière-plan mais publier les données résultantes dans le thread principal ou Activity.

Voici un exemple simple. Si j'ai un Runnable qui ressemble à ceci:

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

    }
}

Comme vous pouvez le voir, ce runnable prend un gestionnaire dans son constructeur. Cela s'appelle depuis un Activity comme ceci:

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

Maintenant, tout ce qui reste est au cœur de cette question, comment vous définissez une classe comme Parcelable. J'ai choisi une classe assez complexe à montrer car il y a des choses que vous ne verriez pas avec une simple. Voici la classe ProductInfo, qui parcelle et décompresse proprement:

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 CREATOR est critique, tout comme le constructeur résultant prenant une parcelle. J'ai inclus les types de données les plus complexes afin que vous puissiez voir comment parceler et décompresser des tableaux d'objets parcellables. C'est une chose courante lorsque vous utilisez Gson pour convertir JSON en objets avec des enfants comme dans cet exemple.

15
David S.

Pour mieux comprendre le concept de colis Essayez le lien ci-dessous

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

j'espère que cela t'aides :)

5
kAnNaN

Moi aussi, j'ai eu un problème similaire. seul l'extrait suivant de emmby et this m'a aidé.

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

Il doit être conservé dans chacune des classes qui implémentent Parcelable

1
A_rmas