4

I am designing an handwriting application for android.

I would like to write information (class LogInfo) into a log file, every time the user presses the enter button.

After that, I would like to read the stored information.

This is part of my class with a custom write method:

public class LogInfo implements Serializable {

private static final long serialVersionUID = -5777674941129067422L;

public static List<Point[][]> strokes;
public static List<byte[]> codes;

// Only write and read methods shown

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);
                //stream.writeObject(elementData[i]);
            }
        }
    }

    int size = codes.size();
    stream.writeInt(size);
    for (int i = 0; i < size; i++)
    {
        stream.write(codes.get(i));
    }
}

This is the read method:

private void readObject(java.io.ObjectInputStream stream)
    {
        stream.defaultReadObject();
        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);
        }

        int codesSize = stream.readInt();
        for (int i = 0; i < codesSize; i++)
        {
            byte[] buffer = new byte[3];
            stream.read(buffer, 0, 3);
            codes.add(buffer);
        }
    }

It works well when I save only one object in the file. When I try to save more, reading is not working (it throws a StreamCorruptedException). It reads only one object in the while loop!

In the main class, I just use two simple methods:

// WRITE TO FILE
logInfo.writeLog();

// READ FROM FILE
ArrayList<LogInfo> logInfoArrayList = logInfo.readLog();

defined as:

public void writeLog()
{
    File file = new File (Environment.getExternalStorageDirectory().getAbsolutePath(), "data.log");
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(file, true);
        //fos = openFileOutput(Environment.getExternalStorageDirectory().getAbsolutePath() + "/data.log", Context.MODE_APPEND);
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(this);
        os.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);  

        LogInfo tempLogInfo = new LogInfo();
        while((tempLogInfo = (LogInfo)reader.readObject()) != null)
            logInfoArray.add(tempLogInfo);
        reader.close();
    } catch (Exception e) {
     //TODO Auto-generated catch block
     e.printStackTrace();
    }

    return logInfoArray;
}

requested UPDATE:

//We use this class to not write a header in a file that already exist
class MyObjectOutputStream extends ObjectOutputStream {

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

    @Override
    protected void writeStreamHeader() {}
}
Friedrich
  • 2,211
  • 20
  • 42
Marek
  • 3,935
  • 10
  • 46
  • 70
  • Could you post the exception stack trace – gZerone May 10 '13 at 01:50
  • 05-10 10:56:40.320: W/System.err(26121): java.io.StreamCorruptedException: Wrong format: ac 05-10 10:56:40.335: W/System.err(26121): at java.io.ObjectInputStream.corruptStream(ObjectInputStream.java:701) 05-10 10:56:40.340: W/System.err(26121): at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:814) 05-10 10:56:40.345: W/System.err(26121): at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2003) 05-10 10:56:40.350: W/System.err(26121): at com.myapp.LogInfo.readLog(LogInfo.java:116) – Marek May 10 '13 at 02:01
  • It is incorrect to loop until null. readObject() doesn't return null at end of stream. It throws EOFException. – user207421 Aug 04 '13 at 00:40

3 Answers3

5
  1. You can't append to an existing file created with an ObjectOutputStream, at least not without effort. There is a trick somewhere about extending ObjectOutputStream and overriding the writeStreamHeader() method so as not to write the stream header the second time, but I'm not in favour of it. You should really rewrite the whole file, maybe as a List.

  2. You don't need all this code. Just make strokes and codes non-static and non-transient, and get rid of the readObject() and writeObject() methods altogether.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Point class is not serializable. I can't resign from overriding readObject() and writeObject() – Marek May 10 '13 at 02:33
  • java.awt.Point is serializable. Are you using a different one? – user207421 May 10 '13 at 02:41
  • I am using import android.graphics.Point. Also, when I make this field non-static, even with those methods I get an exception "Point non serializable". Why...? – Marek May 10 '13 at 02:44
  • Because it tries to serialize them automatically. I would make them non-static and transient. – user207421 May 10 '13 at 02:47
  • Could you explain me more why I can't append to an existig file? I use "true" property in fos = new FileOutputStream(file, true); – Marek May 10 '13 at 03:02
  • I didn't say you can't append to an existing file. I said you can't append to an existing file *created with an `ObjectOutputStream`.* The reason is that `ObjectOutputStream` writes a header which `ObjectInputStream` isn't expecting to see in the middle of a file. – user207421 May 10 '13 at 03:04
  • Could you recommend any other way to write multiple objects to a file? Andd to append new objects at the end of the file? I can't rewrite the whole file, it will be not effective if I will have something like 1000 objects and after creating new one I will have to write them all again... – Marek May 10 '13 at 05:13
  • after deleting "static" from the variable declaration, readObject() method see this object field as null, even that one line before calling readObject I initialized it and checked with debugger to make sure it is not null. Why it is like that? – Marek May 10 '13 at 06:16
  • Because you got a new object from readObject(), not the one you set the value in. – user207421 Aug 04 '13 at 00:39
0

@EJP is right.it cannot append to an existing file created with an ObjectOutputStream. You can do the following steps to fix it: 1. keep ObjectOutputStream object reference 2. after writeObject() having been called, do not call close() 3. provide a method to close ObjectOutputStream.

handrenliang
  • 1,047
  • 1
  • 10
  • 21
0

there is a way to save multiple objects in one file: you should first make an object array (Object [] objects) and then put your objects one-by-one as an Object in this array and then write this array using writeObject(objects) method. good luck.

Mohammad Barbast
  • 1,753
  • 3
  • 17
  • 28
  • There is a problem with this solution when you do not want to store the whole data in the RAM at the same time. It works while you can guarantee that the data fills in RAM. – Dani Feldman Apr 09 '18 at 11:09