Android serializable problem
Asked Answered
C

6

11

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...

Clementina answered 14/5, 2011 at 15:29 Comment(1)
Check out the answer to [this question][1]. It seems to be the same question. [1]:#3628516Literally
G
16

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

Gittens answered 14/5, 2011 at 16:44 Comment(1)
I don't recommend using Parcelable as the Parcel documentation specifically warns against using it as a general-purpose serialization mechanism.Gittens
M
2

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.

Mcrae answered 26/2, 2015 at 10:49 Comment(0)
I
2

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();
}
Importunity answered 4/10, 2018 at 10:45 Comment(0)
F
1

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.

Fanni answered 10/7, 2011 at 9:5 Comment(0)
O
0

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.

Olimpia answered 14/5, 2011 at 15:37 Comment(1)
I would advise against this. From the Parcel docs: "Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable."Overexert
B
0

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

Blessing answered 14/5, 2011 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.