I'm wondering if I should keep one singleton instance of the Gson object for parsing json in my app.
Are there any issues with this approach? Is it premature optimization?
I think both variants are OK, but if you create new Gson
instances too often, it's not very good because of
Gson
instances with custom params in different placesAs now, Gson
instance is thread-safe so you can get single instance of Gson
through Singleton pattern or through DI (dependency injection). And I think that creating few instances of Gson
has the meaning in case you work with difference API/API versions e.g. or if you have to parse same responses differently.
Updated
BUT, be careful, there are some pitfalls that are easy to miss when you trying to use single Gson
instance in multi-threading application. I will explain this with an example. This is just an example, but you have to understand the essence.
Lets imagine the situation you have to specifically serialize your custom class to Json string.
Here is your simple custom class:
public class Foo {
private final Date date;
public Foo(Date date) {
this.date = date;
}
}
And here is custom serializer for Foo
class:
public class FooSerializer implements JsonSerializer<Foo> {
private static SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
@Override
public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) {
final JsonObject object = new JsonObject();
object.addProperty("date", format.format(src.date));
return object;
}
}
As you can notice, there is a static SimpleDateFormat
for serializing Data
objects to String
.
And here is the problem: SimpleDateFormat
is not thread-safe, and typeAdapters
array kept in Gson
object also is not thread-safe.
In this case if you use single Gson
instance
new GsonBuilder()
.registerTypeAdapter(Foo.class, new FooSerializer())
.create();
in different threads in your application, so is only a matter of time, when you get the wrong data, like that: {"date":"Thu, 0015 Oct 2015 21:32:0040 +0600"}
In such cases you can create your own thread-safe object in a similar way:
public class SimpleDateFormatThreadSafe extends SimpleDateFormat {
private static final long serialVersionUID = 5448371898056188202L;
ThreadLocal<SimpleDateFormat> localSimpleDateFormat;
public SimpleDateFormatThreadSafe() {
super();
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat();
}
};
}
public SimpleDateFormatThreadSafe(final String pattern) {
super(pattern);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
}
public SimpleDateFormatThreadSafe(final String pattern, final DateFormatSymbols formatSymbols) {
super(pattern, formatSymbols);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, formatSymbols);
}
};
}
public SimpleDateFormatThreadSafe(final String pattern, final Locale locale) {
super(pattern, locale);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, locale);
}
};
}
@Override
public Object parseObject(String source) throws ParseException {
return localSimpleDateFormat.get().parseObject(source);
}
@Override
public String toString() {
return localSimpleDateFormat.get().toString();
}
@Override
public Date parse(String source) throws ParseException {
return localSimpleDateFormat.get().parse(source);
}
// ... here are other overridden methods ...
@Override
public Object clone() {
return localSimpleDateFormat.get().clone();
}
@Override
public int hashCode() {
return localSimpleDateFormat.get().hashCode();
}
@Override
public boolean equals(Object obj) {
return localSimpleDateFormat.get().equals(obj);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With