Android可序列化问题

Posted

技术标签:

【中文标题】Android可序列化问题【英文标题】:Android serializable problem 【发布时间】:2011-08-25 13:24:48 【问题描述】:

我创建了一个类,它有几个成员变量,所有这些都是可序列化的......除了一个位图!我试图扩展位图并实现可序列化,而不是认为位图是最终类。

我想保存这个类(它基本上构成了游戏的当前状态),以便玩家可以拿起并加载游戏。

在我看来,我有两个选择: 1)寻找另一种保存游戏状态的方法。如有任何帮助,我们将不胜感激。

2) 将位图成员变量更改为 int,例如,并创建一个 BitmapGetter 类,该类具有返回基于 int 的位图的静态方法。 (这个选项并不容易,因为我的课程包含很多位图可能性,而我创建游戏的方式意味着这将需要大量的努力。

基本上没有人可以责怪我自己懒惰地不假思索地创建了一个位图变量,但我会很感激任何帮助......

【问题讨论】:

查看[这个问题][1]的答案。这似乎是同一个问题。 [1]:***.com/questions/3628016/… 【参考方案1】:

用这样的类替换 Bitmap 怎么样:

public class SerialBitmap implements Serializable 

    public Bitmap bitmap;

    // TODO: Finish this constructor
    SerialBitmap(<some params>) 
        // Take your existing call to BitmapFactory and put it here
        bitmap = BitmapFactory.decodeSomething(<some params>);
    

    // Converts the Bitmap into a byte array for serialization
    private void writeObject(java.io.ObjectOutputStream out) throws IOException 
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteStream);
        byte bitmapBytes[] = byteStream.toByteArray();
        out.write(bitmapBytes, 0, bitmapBytes.length);
    

    // Deserializes a byte array representing the Bitmap and decodes it
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException 
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        int b;
        while((b = in.read()) != -1)
            byteStream.write(b);
        byte bitmapBytes[] = byteStream.toByteArray();
        bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
    

重写的 Serializable.writeObject() 和 readObject() 方法序列化字节而不是位图,因此类是可序列化的。您将需要完成构造函数,因为我不知道您当前是如何构造位图的。最后要做的是用 YourClass.serialBitmap.bitmap 替换对 YourClass.bitmap 的引用。

祝你好运!

巴里 附言这段代码可以编译,但我没有用真正的位图测试过

【讨论】:

我不建议使用 Parcelable,因为 Parcel 文档特别警告不要将其用作通用序列化机制。【参考方案2】:

我也遇到了同样的问题。

我决定这样。

BitmapParcelable,所以我跟着我的班级。

    我创建了 Constructor ,它获取了 Bundle object 和 getter 返回代表对象数据的 Bundle。所以虽然 Bitmapparcelable , Bundle 可以将bitmap 保存为parcelable 在里面。

    当需要在intent中传递Date时,可以调用objects getBundle() 方法和传递 Intent.putExtra(String key,Bundle value)

    在目标活动中,您将调用 getBundle(String key) 并传递它 到构造函数

    我认为这是非常简单的方法。

【讨论】:

【参考方案3】:

如果可以在应用程序中单独保存位图数据,可以执行以下操作:

在保存当前状态的类中,将位图保存到您选择的文件夹中:

FileOutputStream out = new FileOutputStream(<path to bmp>);
bitmap.compress(CompressFormat.PNG, 100, out);

在以位图为成员的类中,以路径为可序列化成员,反序列化后重构位图:

public class MyClass implements Serializable

    // ...
    private String bitmapPath;
    transient Bitmap bitmap;
    // ...

    private void writeObject(ObjectOutputStream out) throws IOException
    
        out.defaultWriteObject();
    

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    
        in.defaultReadObject();
        bitmap = BitmapFactory.decodeFile(path);
    

如果需要,您可以在 readObject() 函数中实现任何其他构建功能,因为在 defaultReadObject() 调用之后完全构造了对象。

希望这会有所帮助。

顺便说一句,http://developer.android.com/reference/android/os/Parcel.html 建议不要将Parcelable 用于序列化目的。我还没有足够的积分来发表评论,所以我正在编辑自己的答案以发表此评论。

【讨论】:

【参考方案4】:

这是一个通用的位图包装器:(编辑自 Barry Fruitman 回答)

    public class SerialBitmap implements Serializable 

    private Bitmap bitmap;
    private transient Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.PNG;
    private transient int compressQuality = 100;

    public SerialBitmap(Bitmap bitmap)
    
        this.bitmap = bitmap;
    

    public Bitmap getBitmap() 
        return bitmap;
    

    public void recycle() 
        if (bitmap!=null && !bitmap.isRecycled()) bitmap.recycle();
    
    private void writeObject(java.io.ObjectOutputStream out) throws IOException 

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(compressFormat, compressQuality, stream);

        byte[] byteArray = stream.toByteArray();

        out.writeInt(byteArray.length);
        out.write(byteArray);

    

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException 


        int bufferLength = in.readInt();

        byte[] byteArray = new byte[bufferLength];

        int pos = 0;
        do 
            int read = in.read(byteArray, pos, bufferLength - pos);

            if (read != -1) 
                pos += read;
             else 
                break;
            

         while (pos < bufferLength);

        bitmap = BitmapFactory.decodeByteArray(byteArray, 0, bufferLength);

    

    public Bitmap.CompressFormat getCompressFormat() 
        return compressFormat;
    

    public void setCompressFormat(Bitmap.CompressFormat compressFormat) 
        this.compressFormat = compressFormat;
    

    public int getCompressQuality() 
        return compressQuality;
    

    public void setCompressQuality(int compressQuality) 
        this.compressQuality = compressQuality;
    

如果你想压缩位图并使串行对象更小 您可以通过setCompressFormatsetCompressQuality 设置压缩。

例子:

setCompressFormat(Bitmap.CompressFormat.JPEG);
setCompressQuality(80);

如果您使用的是 Progourd,请添加以下规则:

-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable 
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();

【讨论】:

【参考方案5】:

首先,您应该通过Parcelable 进行序列化。它是一个 Android 类,通常开箱即用,效果很好:你可以用它序列化一个 ByteArray,方法如下:

public final void writeByteArray (byte[] b)

public final void readByteArray (byte[] val)

您可能还想查看Parcel 文档。

【讨论】:

我建议不要这样做。来自 Parcel 文档:“Parcel 不是通用的序列化机制。此类(以及用于将任意对象放入 Parcel 的相应 Parcelable API)被设计为高性能 IPC 传输。因此,不适合将任何 Parcel 数据放入持久存储:Parcel 中任何数据的底层实现发生变化都可能导致旧数据不可读。”【参考方案6】:

您可以使用以下 java 方法手动进行序列化:

private void writeObject(java.io.ObjectOutputStream out)
private void readObject(java.io.ObjectInputStream in)

使用 getPixels 序列化位图,当您进行反序列化时,您可以使用 createBitmap 从头开始​​重新创建它。

您可以在此处阅读有关如何使用 readObject 和 writeObject 的信息:http://download.oracle.com/javase/6/docs/api/java/io/Serializable.html

【讨论】:

以上是关于Android可序列化问题的主要内容,如果未能解决你的问题,请参考以下文章

Android - 具有可序列化对象的 SharedPreferences

Android Bundle 无法将可序列化对象识别为已序列化

Android - 在不同apk上的服务之间传递可序列化或可打包的捆绑包

自定义对象的 Android ArrayList - 保存到 SharedPreferences - 可序列化?

使用android中的可序列化将对象从第二个活动传递回主要活动

序列化与 Parcelable Android