1
\$\begingroup\$

I couldn't find a good example, so after some fighting with writing non-serializable object to file in Android, I decided to show you my solution. Could you tell me if it is OK or how could it be improved?

This class is used to store information in a file. writeObject and readObject methods were created, because android.graphics.Point is not serializable. This is the reason why strokes was declared as transient. To make sure that we are able to overwrite file, we need to create our own ObjectOutputStream, which overrides writeStreamHeader, so the second header will not be appended to our file. We check if the file size is greater that zero, and according to this we create our OutputStream.

In general, what I understand is that to write a non-serializable object to file we need to make it transient and define our readObjectand writeObject methods. To overwrite a file we need to make our own ObjectOutputStream.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import android.graphics.Point;
import android.os.Environment;

public class LogInfo implements Serializable{

    private static final long serialVersionUID = 710487907869630527L;

    transient public ArrayList<Point[][]> strokes;

    public LogInfo()
    {
        strokes = new ArrayList<Point[][]>();
    }

    private void writeObject(ObjectOutputStream stream) throws IOException 
    {
        stream.defaultWriteObject();
        stream.writeInt(strokes.size());
        Point[][] pointsArray = null;
        for (int i = 0; i < strokes.size(); i++)
        {
            pointsArray = ((Point[][])strokes.get(i));
            stream.writeInt(pointsArray.length);
            for (int j = 0; j < pointsArray.length; j++)
            {
                stream.writeInt(pointsArray[j].length);
                for (int k = 0; k < pointsArray[j].length; k++)
                {
                    stream.writeInt(pointsArray[j][k].x);
                    stream.writeInt(pointsArray[j][k].y);
                }
            }
        }
    }

    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException, ClassCastException
    {
        stream.defaultReadObject();
        strokes = new ArrayList<Point[][]>();
        int strokesSize = stream.readInt();
        for (int i = 0; i < strokesSize; i++)
        {
            int arrayXSize = stream.readInt();
            Point[][] points = new Point[arrayXSize][];
            for (int j = 0; j < arrayXSize; j++)
            {
                int arrayYSize = stream.readInt();
                points[j] = new Point[arrayYSize];
                for (int k = 0; k < arrayYSize; k++)
                    points[j][k] = new Point(stream.readInt(), stream.readInt());
            }
            strokes.add(points);
        }
    }

    public void writeLog()
    {
        File file = new File (Environment.getExternalStorageDirectory().getAbsolutePath(), "data.log");
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(file, true);
            ObjectOutputStream writer;
            if(file.length() > 0)
            {
                writer = new MyObjectOutputStream(fos);
            }
            else
            {
                writer = new ObjectOutputStream(fos);
            }
            writer.writeObject(this);
            writer.close(); 
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public ArrayList<LogInfo> readLog()
    {
        ArrayList<LogInfo> logInfoArray = new ArrayList<LogInfo>();
        try{
            File file = new File (Environment.getExternalStorageDirectory().getAbsolutePath(), "data.log");
            FileInputStream fis  = new FileInputStream(file);
            ObjectInputStream reader = new ObjectInputStream(fis);

            while (file.canRead())
            {
                logInfoArray.add((LogInfo) reader.readObject());
            }
            reader.close();
        } catch (Exception e) {
         //TODO Auto-generated catch block
         e.printStackTrace();
        }

        return logInfoArray;
    }
}

class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream(OutputStream os) throws IOException {
        super(os);
      }

    @Override
    protected void writeStreamHeader() {}
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Just a small detail: you don't need to make strokes transient. When you override readObject/writeObject, you completely override the usual serialization mechanism.

Transient just means that the usual serialization mechanism should skip that member when marshalling/unmarshalling the whole object.

Otherwise, it looks pretty good. I haven't checked explicitly that readObject/writeObject process everything in the same order, but hopefully you made some unit test that checks for that.

\$\endgroup\$
1
  • \$\begingroup\$ If strokes are not transient, it tries to serialize them by default and it does not work. It only works when they are transient... \$\endgroup\$
    – Marek
    Commented May 16, 2013 at 3:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.