Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android serializable problem

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

like image 862
JXXH Avatar asked May 14 '11 15:05

JXXH


4 Answers

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

like image 180
Barry Fruitman Avatar answered Nov 10 '22 10:11

Barry Fruitman


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.

like image 27
Ioane Sharvadze Avatar answered Nov 10 '22 10:11

Ioane Sharvadze


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();
}
like image 45
Gil SH Avatar answered Nov 10 '22 10:11

Gil SH


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.

like image 31
alokoko Avatar answered Nov 10 '22 10:11

alokoko