【问题标题】:How to use Parcel in Android?如何在 Android 中使用 Parcel?
【发布时间】:2009-10-26 18:53:28
【问题描述】:

我正在尝试使用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);
    }


}

我错过了什么?

更新:为了简化测试,我在原始示例中删除了文件的读取和写入。

【问题讨论】:

    标签: android parcel


    【解决方案1】:

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

    1. CREATOR 必须是公开的,而不是受保护的。但更重要的是,
    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);
        }
    
    
    }
    

    【讨论】:

    • 你刚刚拯救了我的一天。谢谢。
    • 只是不要在 Parcel(able) 中使用位图尝试这个:(
    • 只是一个普通的一天@与 Android 一起工作。你回答石头。
    【解决方案2】:

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

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

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

    【讨论】:

    • 你能推荐一种替代的序列化机制吗?
    • @aaronsnoswell 我推荐使用 Kryo,或者实现 Java Externalizable 接口(如果你的对象不复杂)
    • @DShaw 你不应该为此使用 Serializable ;在升级 java 版本和读取旧的序列化数据时,或者升级你的软件时会造成很多麻烦
    【解决方案3】:

    我发现 Parcelable 最常用于 Android 中的数据包中,但更具体地用于发送和接收消息的处理程序中。例如,您可能有一个 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 类,它可以干净地包裹和取消包裹:

    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 也是如此。我包含了更复杂的数据类型,因此您可以了解如何对 Parcelable 对象的 Parcel 和 unParcel 数组进行 Parcel 和 unParcel 数组。这在使用 Gson 将 JSON 转换为具有子对象的对象时很常见,如本例所示。

    【讨论】:

    • 如何使用androidParcelable界面的最佳解释。
    【解决方案4】:

    我也有类似的问题。只有来自 emmbythis 的以下 sn-p 帮助了我。

        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 的类中

    【讨论】:

      【解决方案5】:

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

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

      希望这会有所帮助:)

      【讨论】:

        猜你喜欢
        • 2021-06-15
        • 1970-01-01
        • 2015-02-21
        • 1970-01-01
        • 2020-04-09
        • 1970-01-01
        • 1970-01-01
        • 2013-09-28
        • 2020-09-08
        相关资源
        最近更新 更多