开发者

Android serializable problem

开发者 https://www.devze.com 2023-03-05 17:23 出处:网络
I created a class, which has several member variables, all of which are serializable... except one Bitmap! I tried to extend bitmap and implement serializable, not thinking Bitmap is a final class.

I created a class, which has several member variables, all of which are serializable... except one Bitmap! I tried to extend bitmap and implement serializable, not thinking Bitmap is a final class.

I want to save the class (it basically forms the current state of a game) so a player can pick-up and load the game.

The way I see it I have two options: 1) Find another way to save the game state. Any help here would 开发者_运维问答be appreciated.

2) change the bitmap member variable to an int, say, and create a BitmapGetter class that has a static method returning bitmaps based on ints. (This option is not easy, as my class contains so many bitmap possiblities and the way I created the game means this will require an incredible amount of effort.

Basically I have no one to blame but myself for lazily creating a bitmap variable without thinking, but I would appreciate any help...


How about replacing Bitmap with a class like this:

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

The overridden Serializable.writeObject() and readObject() methods serialize the bytes instead of the Bitmap so the class is serializable. You will need to finish the constructor because I don't know how you currently construct your Bitmap. The last thing to do is to replace references to YourClass.bitmap with YourClass.serialBitmap.bitmap.

Good luck!

Barry P.S. This code compiles but I haven't tested it with a real bitmap


I had the same problem.

And i decided like this.

Bitmap is Parcelable, so I made following to my class.

  1. I made Constructor which gets Bundle object , and getter that returns Bundle representing Objects data. So while Bitmap is parcelable , Bundle can save bitmap as parcelable in it.

  2. When you need to pass Date in intent , you can call objects getBundle() method and pass with Intent.putExtra(String key,Bundle value)

  3. In target activity you will call getBundle(String key) and pass it to constructor.

    I think it's very easy approach.


Here is a general bitmap wrapper: (Edit from Barry Fruitman answer)

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

if you want to compress the bitmap and make the serial object smaller you can set the compression via setCompressFormat and setCompressQuality.

Example:

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

If you are using Progourd, add the following rules:

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


If it is OK to save the bitmap data separately in your application, you can do the following:

In your class that saves the current state, save the bitmap to a folder of your choice:

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

In the class that has the bitmap as member, have the path as the serializable member and reconstruct the bitmap after deserialization:

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

You can implement any other build-up functionality in the readObject() function if needed, since the object is fully constructed after the defaultReadObject() call.

Hope this helps.

BTW, http://developer.android.com/reference/android/os/Parcel.html recommends against using Parcelable for serialization purposes. I do not have enough points yet to leave a comment, so I am editing my own answer to put in this remark.


First of all, you should serialize via Parcelable. It's an Android class and it usually works great, out of the box: and you can serialize a ByteArray with it, with the methods:

public final void writeByteArray (byte[] b)

and

public final void readByteArray (byte[] val)

You might want to check Parcel documentation too.


You can do the serialization by hand by using the following java methods:

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

Serialize the bitmap by using getPixels and when you do the deserialization you can use createBitmap to recreate it from scratch.

You can read about on how to use readObject and writeObject here: http://download.oracle.com/javase/6/docs/api/java/io/Serializable.html

0

精彩评论

暂无评论...
验证码 换一张
取 消