0
votes

I'm on android and I'm trying to save a list of objects into a file but I always get this exception: java.io.NotSerializableException: android.app.Application.

I've tried to find what could cause this but all I found is that this error is thrown when trying to serialize a Context object but I'm not even trying to serialize anything related to Application or Context.

DataFileManager.java

public class DataFileManager implements Serializable {
    public static final String FILE_NAME = "circles.dat";
    private transient final Context ctxt;
    private Set<Save> saves;
    public DataFileManager(Context ctxt) {
        this.ctxt = ctxt;
        this.saves = new HashSet<>();
        reloadSaves();
    }

    public void reloadSaves() {
        File file = file();
        if(file.exists()) {
            try (ObjectInputStream reader = new ObjectInputStream(
                    new BufferedInputStream(ctxt.openFileInput(FILE_NAME)))) {
                saves.clear();
                try {
                    while (true) {
                        Save save = (Save) reader.readObject();
                        saves.add(save);
                    }
                } catch(EOFException e) {
                } finally {
                    reader.close();
                }
            } catch (IOException | ClassNotFoundException e) {
                Log.e(e.getClass().getName(), "", e);
            }
        } else
            Log.e("load", "file does not exist");
    }

    private void save() {
        try {
            file().createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(e.getClass().getName(), "", e);
        }
        try (ObjectOutputStream writer = new ObjectOutputStream(new BufferedOutputStream(ctxt.openFileOutput(FILE_NAME, Context.MODE_PRIVATE)))) {
            Iterator<Save> iter = saves.iterator();
            while(iter.hasNext()) {
                Save save = iter.next();
                writer.writeObject(save);
            }
            writer.flush();
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e(e.getClass().getName(), "", e);
        } catch (IOException e) {
            Log.e(e.getClass().getName(), "", e);
        }
    }


    private File file() {
        return ctxt.getFileStreamPath(FILE_NAME);
    }


    public Save save(String saveName, Set<Circle> circles) throws SaveAlreadyExistsException {
        if(exists(saveName))
            throw new SaveAlreadyExistsException();
        Save save = createSave(saveName, circles);
        if(!saves.add(save))
            throw new SaveAlreadyExistsException();
        return save;
    }

    public Save forceSave(String saveName, Set<Circle> circles) throws UnknownSaveException {
        Save save;
        if(exists(saveName))
            (save = find(saveName)).overwrite(circles);
        else {
            save = createSave(saveName, circles);
            if(!saves.add(save)) {
                saves.remove(save);
                if(!saves.add(save))
                    throw new UnknownSaveException("Couldn't add Save object to the list");
            }
            save();
        }
        return save;
    }

    private Save createSave(String name, Set<Circle> circles) {
        Save save = new Save(name);
        save.circles.addAll(circles);
        return save;
    }

    public boolean exists(String saveName) {
        return saves.stream().anyMatch(s -> s.getName().equals(saveName));
    }

    public boolean existsIgnoreCase(String saveName) {
        return saves.stream().anyMatch(s -> s.getName().equalsIgnoreCase(saveName));
    }

    public Save find(String saveName) {
        return saves.stream().filter(s -> s.getName().equals(saveName)).findFirst().orElse(null);
    }

    public List<Save> search(String query) {
        return saves.stream().filter(s -> s.getName().toLowerCase().contains(query.toLowerCase()))
                    .collect(Collectors.toList());
    }

    public Set<Save> getSaves() {
        return Collections.unmodifiableSet(saves);
    }



    public class Save implements Serializable {
        private final String      name;
        private final Set<Circle> circles;

        private Save(String name) {
            this.name = name;
            circles = new HashSet<>();
        }

        public boolean overwrite(Set<Circle> circles) {
            if (circles != null && circles.size() > 0) {
                this.circles.clear();
                this.circles.addAll(circles);
                save();
                return true;
            }
            return false;
        }

        public Set<Circle> getCircles() {
            return circles.stream().map(Circle::clone).collect(Collectors.toSet());
        }

        public String getName() {
            return name;
        }

        @Override
        public boolean equals(Object obj) {
            return obj != null && obj instanceof Save && ((Save)obj).name.equals(this.name);
        }
    }

    public static class SaveAlreadyExistsException extends UnknownSaveException {
        public SaveAlreadyExistsException() {
            super("save already exists");
        }
    }

    public static class UnknownSaveException extends Exception {
        private static final String DFLT_MSG = "An unknown error occured";

        public UnknownSaveException() {
            super(DFLT_MSG);
        }

        public UnknownSaveException(String message) {
            super(message);
        }

        public UnknownSaveException(String message, Throwable cause) {
            super(message, cause);
        }

        public UnknownSaveException(Throwable cause) {
            super(DFLT_MSG, cause);
        }
    }
}

Circle.java

public class Circle implements Cloneable, Serializable {
    private static long nextID = 0;
    private float radius, centerX, centerY;
    private final long id;

    public Circle(float centerX, float centerY) {
        this(centerX, centerY, nextID++);
    }

    private Circle(float centerX, float centerY, long id) {
        this.radius = 0;
        this.centerX = centerX;
        this.centerY = centerY;
        this.id = id;
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public float getCenterX() {
        return centerX;
    }

    public void setCenterX(float centerX) {
        this.centerX = centerX;
    }

    public float getCenterY() {
        return centerY;
    }

    public void setCenterY(float centerY) {
        this.centerY = centerY;
    }

    @Override public Circle clone() {
        return new Circle(getCenterX(), getCenterY(), id);
    }

    public boolean isCloneOf(Circle circle) {
        return circle != null && circle != this && this.id == circle.id;
    }

    @Override
    public boolean equals(Object obj) {
        return obj != null && obj instanceof Circle && !isCloneOf((Circle)obj);
    }
}

StackTrace

E/java.io.NotSerializableException: java.io.NotSerializableException: android.app.Application at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1224) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346) at com.multimedia.tp3.model.files.DataFileManager.save(DataFileManager.java:77) at com.multimedia.tp3.model.files.DataFileManager.forceSave(DataFileManager.java:116) at com.multimedia.tp3.DrawSurface.save(DrawSurface.java:131) at com.multimedia.tp3.MainActivity.onActivityResult(MainActivity.java:95) at android.app.Activity.dispatchActivityResult(Activity.java:6996) at android.app.ActivityThread.deliverResults(ActivityThread.java:4069) at android.app.ActivityThread.handleSendResult(ActivityThread.java:4116) at android.app.ActivityThread.-wrap20(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1516) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:159) at android.app.ActivityThread.main(ActivityThread.java:6097) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

1
Is Circle a standalone Java class, or is it an inner class inside something else?CommonsWare
@CommonsWare Circle is an inner class of DataFileManagerElie G.
You are serializing an inner class somewhere. file().createNewFile(); is pointless here, and does not throw exceptions. You don't need to iterate the set: you can save the whole thing in one go.user207421

1 Answers

3
votes

it's an inner class of DataFileManager

Make it be public static class instead of public class. Right now, your serialization is choking on the DataFileManager that is being serialized, as every Circle has an implicit reference back to the outer class instance that created it.