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.
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.
When you need to pass Date in intent , you can call objects
getBundle()
method and pass with Intent.putExtra(String key,Bundle value)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With