Comment sérialiser un objet et l'enregistrer dans un fichier sous Android?

128

Disons que j'ai une classe simple et qu'une fois qu'elle est instanciée en tant qu'objet, je veux pouvoir sérialiser son contenu dans un fichier, et le récupérer en chargeant ce fichier ultérieurement ... Je ne sais pas par où commencer ici, que dois-je faire pour sérialiser cet objet dans un fichier?

public class SimpleClass {
   public string name;
   public int id;
   public void save() {
       /* wtf do I do here? */
   }
   public static SimpleClass load(String file) {
       /* what about here? */
   }
}

C'est probablement la question la plus simple au monde, car c'est une tâche très simple dans .NET, mais sous Android, je suis assez nouveau, donc je suis complètement perdu.

Ben Lesh
la source

Réponses:

250

Sauvegarde (sans code de gestion des exceptions):

FileOutputStream fos = context.openFileOutput(fileName, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(this);
os.close();
fos.close();

Chargement (sans code de gestion des exceptions):

FileInputStream fis = context.openFileInput(fileName);
ObjectInputStream is = new ObjectInputStream(fis);
SimpleClass simpleClass = (SimpleClass) is.readObject();
is.close();
fis.close();
Ralkie
la source
Très utile. Pouvez-vous expliquer si nous devons sérialiser la classe pour l'écrire en tant que fichier objet.
Arun Chettoor
4
Cette fonctionnalité est implicitement ajoutée à votre classe si vous utilisez l'interface Serializable. Si tout ce que vous voulez, c'est une simple sérialisation d'objets, c'est ce que j'utiliserais. Il est également extrêmement facile à mettre en œuvre. developer.android.com/reference/java/io/Serializable.html
mtmurdock
6
+1, Pour la sauvegarde de plusieurs objets, une astuce est requise: stackoverflow.com/a/1195078/1321401
Luten
2
Devrait-il y avoir un appel à fos.close () et fis.close () également?
IcedDante
Je recommanderais Paper . Il utilise la sérialisation Kryo et beaucoup plus rapide que la sérialisation Java courante.
Aleksey Masny
36

J'ai essayé ces 2 options (lecture / écriture), avec des objets simples, un tableau d'objets (150 objets), une carte:

Option 1:

FileOutputStream fos = context.openFileOutput(fileName, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(this);
os.close();

Option 2:

SharedPreferences mPrefs=app.getSharedPreferences(app.getApplicationInfo().name, Context.MODE_PRIVATE);
SharedPreferences.Editor ed=mPrefs.edit();
Gson gson = new Gson(); 
ed.putString("myObjectKey", gson.toJson(objectToSave));
ed.commit();

L'option 2 est deux fois plus rapide que l'option 1

L'inconvénient de l'option 2 est que vous devez créer un code spécifique pour la lecture:

Gson gson = new Gson();
JsonParser parser=new JsonParser();
//object arr example
JsonArray arr=parser.parse(mPrefs.getString("myArrKey", null)).getAsJsonArray();
events=new Event[arr.size()];
int i=0;
for (JsonElement jsonElement : arr)
    events[i++]=gson.fromJson(jsonElement, Event.class);
//Object example
pagination=gson.fromJson(parser.parse(jsonPagination).getAsJsonObject(), Pagination.class);
surfealokesea
la source
3
Pourquoi diriez-vous que l'option 2 est plus rapide? Peut-être parce que SharedPreferences est conservé en mémoire et que le temps que vous avez mesuré n'incluait pas son enregistrement dans le système de fichiers? Je pose cette question car j'imagine que la sérialisation vers un flux d'objets doit être plus efficace que vers une chaîne JSON.
CesarPim
10

Je viens de créer une classe pour gérer cela avec Generics, afin qu'elle puisse être utilisée avec tous les types d'objets sérialisables:

public class SerializableManager {

    /**
     * Saves a serializable object.
     *
     * @param context The application context.
     * @param objectToSave The object to save.
     * @param fileName The name of the file.
     * @param <T> The type of the object.
     */

    public static <T extends Serializable> void saveSerializable(Context context, T objectToSave, String fileName) {
        try {
            FileOutputStream fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

            objectOutputStream.writeObject(objectToSave);

            objectOutputStream.close();
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Loads a serializable object.
     *
     * @param context The application context.
     * @param fileName The filename.
     * @param <T> The object type.
     *
     * @return the serializable object.
     */

    public static<T extends Serializable> T readSerializable(Context context, String fileName) {
        T objectToReturn = null;

        try {
            FileInputStream fileInputStream = context.openFileInput(fileName);
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            objectToReturn = (T) objectInputStream.readObject();

            objectInputStream.close();
            fileInputStream.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return objectToReturn;
    }

    /**
     * Removes a specified file.
     *
     * @param context The application context.
     * @param filename The name of the file.
     */

    public static void removeSerializable(Context context, String filename) {
        context.deleteFile(filename);
    }

}
Sandro Machado
la source
7

Le code complet avec gestion des erreurs et ajout de flux de fichiers se ferme. Ajoutez-le à votre classe que vous souhaitez pouvoir sérialiser et désérialiser. Dans mon cas, le nom de la classe est CreateResumeForm. Vous devriez le changer en votre propre nom de classe. AndroidL'interface Serializablen'est pas suffisante pour enregistrer vos objets dans le fichier, elle ne crée que des flux.

// Constant with a file name
public static String fileName = "createResumeForm.ser";

// Serializes an object and saves it to a file
public void saveToFile(Context context) {
    try {
        FileOutputStream fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(this);
        objectOutputStream.close();
        fileOutputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}


// Creates an object by reading it from a file
public static CreateResumeForm readFromFile(Context context) {
    CreateResumeForm createResumeForm = null;
    try {
        FileInputStream fileInputStream = context.openFileInput(fileName);
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        createResumeForm = (CreateResumeForm) objectInputStream.readObject();
        objectInputStream.close();
        fileInputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return createResumeForm;
}

Utilisez-le comme ceci dans votre Activity:

form = CreateResumeForm.readFromFile(this);
Denis Kutlubaev
la source
0

J'utilise SharePrefrences:

package myapps.serializedemo;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.io.IOException;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

//Create the SharedPreferences
    SharedPreferences sharedPreferences = this.getSharedPreferences("myapps.serilizerdemo", Context.MODE_PRIVATE);
    ArrayList<String> friends = new ArrayList<>();
    friends.add("Jack");
    friends.add("Joe");
    try {

 //Write / Serialize
 sharedPreferences.edit().putString("friends",
    ObjectSerializer.serialize(friends)).apply();
    } catch (IOException e) {
        e.printStackTrace();
    }
//READ BACK
    ArrayList<String> newFriends = new ArrayList<>();
    try {
        newFriends = (ArrayList<String>) ObjectSerializer.deserialize(
                sharedPreferences.getString("friends", ObjectSerializer.serialize(new ArrayList<String>())));
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.i("***NewFriends", newFriends.toString());
}
}
Zod
la source
0

Vous devez ajouter une classe ObjectSerialization à votre programme, ce qui suit peut fonctionner

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;

    public class ObjectSerializer {

public static String serialize(Serializable obj) throws IOException {
    if (obj == null) return "";
    try {
        ByteArrayOutputStream serialObj = new ByteArrayOutputStream();
        ObjectOutputStream objStream = new ObjectOutputStream(serialObj);
        objStream.writeObject(obj);
        objStream.close();
        return encodeBytes(serialObj.toByteArray());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public static Object deserialize(String str) throws IOException {
    if (str == null || str.length() == 0) return null;
    try {
        ByteArrayInputStream serialObj = new ByteArrayInputStream(decodeBytes(str));
        ObjectInputStream objStream = new ObjectInputStream(serialObj);
        return objStream.readObject();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public static String encodeBytes(byte[] bytes) {
    StringBuffer strBuf = new StringBuffer();

    for (int i = 0; i < bytes.length; i++) {
        strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
        strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
    }

    return strBuf.toString();
}

public static byte[] decodeBytes(String str) {
    byte[] bytes = new byte[str.length() / 2];
    for (int i = 0; i < str.length(); i+=2) {
        char c = str.charAt(i);
        bytes[i/2] = (byte) ((c - 'a') << 4);
        c = str.charAt(i+1);
        bytes[i/2] += (c - 'a');
    }
    return bytes;
}

}

si vous utilisez pour stocker un tableau avec SharedPreferences, utilisez ce qui suit: -

SharedPreferences sharedPreferences = this.getSharedPreferences(getPackageName(),MODE_PRIVATE);

Pour sérialiser: -

sharedPreferences.putString("name",ObjectSerializer.serialize(array));

Pour désérialiser: -

newarray = (CAST_IT_TO_PROPER_TYPE) ObjectSerializer.deSerialize(sharedPreferences.getString(name),null);
Priyanshu Dubey
la source