I realized I had just created two classes that were virtually identical for simply saving objects to their own json files, so I've attempted to make it more generic. My only noticeable problem is because I am using gson to serialize / deserialize, that requires an explicit class type, which gets squashed if I use the generic T. So I'm using a @StringDef
annotation so the class can figure out which file and object I want to serialize / deserialize - couldn't really thing of a nice way of doing but would like to know what people think:
public class Storage<T> {
//Objects my app can serialize
public static final String BUSINESS = "b.json";
public static final String TRANSACTION = "t.json";
@StringDef({BUSINESS, TRANSACTION})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
private static final String TAG = "Storage";
private TSerializer mSerializer;
public Storage(Context appContext, @Type String type) {
mSerializer = new TSerializer(appContext, type);
}
public ArrayDeque<T> getFromStorage() {
try {
return mSerializer.load();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
public T getLastFromStorage() {
ArrayDeque<T> array = getFromStorage();
if (array != null && array.size() > 0)
return array.getLast();
return null;
}
public boolean saveItem(T item) {
try {
mSerializer.save(item);
Log.d(TAG, "Item saved to file");
return true;
} catch (Exception e) {
Log.e(TAG, "Error saving Item", e);
return false;
}
}
public boolean wipeItems() {
try {
mSerializer.wipe();
return true;
} catch (Exception e) {
Log.e(TAG, "Error wiping storage", e);
return false;
}
}
public void saveItems(ArrayDeque<T> items) {
if (items != null && items.size() > 0) {
for (T t : items)
saveItem(t);
}
}
private class TSerializer {
private Context mContext;
@Storage.Type private String mType;
private TSerializer(Context c, @Storage.Type String type) {
mContext = c;
mType = type;
}
private void save(T item)
throws JSONException, IOException {
ArrayDeque<T> tList = load();
if (tList == null)
tList = new ArrayDeque<>();
tList.add(item);
saveList(tList);
}
private void save(ArrayDeque<T> items)
throws JSONException, IOException {
ArrayDeque<T> tList = load();
if (tList == null)
tList = new ArrayDeque<>();
tList.addAll(items);
saveList(tList);
}
private void saveList(ArrayDeque<T> items)
throws JSONException, IOException {
String json = new Gson().toJson(items);
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(json).getAsJsonArray();
String tString = array.toString();
Writer writer = null;
try {
OutputStream out = mContext.openFileOutput(mType, Context.MODE_PRIVATE);
writer = new OutputStreamWriter(out);
writer.write(tString);
} finally {
if (writer != null)
writer.close();
}
}
private void wipe()
throws JSONException, IOException {
Writer writer = null;
try {
OutputStream out = mContext.openFileOutput(mType, Context.MODE_PRIVATE);
writer = new OutputStreamWriter(out);
writer.write("");
} finally {
if (writer != null)
writer.close();
}
}
private ArrayDeque<T> load() throws IOException, JSONException {
ArrayDeque<T> transactions = null;
BufferedReader reader = null;
try {
//Open and read the file into a string builder
InputStream in = mContext.openFileInput(mType);
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder jsonString = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
//Line breaks are omitted and irrelevant
jsonString.append(line);
}
java.lang.reflect.Type type;
switch (mType) {
case Storage.BUSINESS:
type = new TypeToken<ArrayDeque<Business>>() {
}.getType();
break;
case Storage.TRANSACTION:
type = new TypeToken<ArrayDeque<Transaction>>() {
}.getType();
break;
default:
type = new TypeToken<ArrayDeque<Business>>() {
}.getType();
break;
}
transactions = new Gson().fromJson(jsonString.toString(), type);
} catch (FileNotFoundException e) {
//Ignore this one, happens when launching for the first time
} finally {
if (reader != null)
reader.close();
}
return transactions;
}
}
I then save / retrieve like so:
//save
Storage<Business> s = new Storage<>(getContext(), Storage.BUSINESS);
s.saveItem(b);
//retrieve
Business b = s.getLastFromStorage();