如何在Android中使用Parcel?

时间:2009-10-26 18:53:28

标签: android parcel

我正在尝试使用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 个答案:

答案 0 :(得分:68)

啊,我终于找到了问题。实际上有两个。

  1. CREATOR必须是公开的,不受保护。但更重要的是,
  2. 取消编组数据后,您必须致电setDataPosition(0)
  3. 以下是修订后的工作代码:

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

答案 1 :(得分:20)

小心!不要使用Parcel序列化到文件

  

Parcel不是通用序列化机制。此类(以及用于将任意对象放入包中的相应Parcelable API)被设计为高性能IPC传输。因此,将任何Parcel数据放入持久存储中是不合适的:Parcel中任何数据的底层实现的更改都可能导致旧数据不可读。

来自http://developer.android.com/reference/android/os/Parcel.html

答案 2 :(得分:15)

我发现Parcelable最常用于数据包中的Android,但更具体地说是在发送和接收消息的Handler中。例如,您可能需要在后台运行AsyncTaskRunnable,但会将结果数据发布到主线程或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在其构造函数中使用了Handler。这是从某些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类,Parcels和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是至关重要的,因为生成的构造函数采用了包裹。我包含了更复杂的数据类型,因此您可以看到如何Parcel和UnParcel Parrays对象的数组。当使用Gson将JSON转换为带子节点的对象时,这是常见的事情。

答案 3 :(得分:5)

要更好地了解包裹概念,请尝试以下链接

  

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

希望这有助于:)

答案 4 :(得分:1)

我也有类似的问题。只有 emmby this的以下片段帮助了我。

    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

的类中