Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On Android, should/can I use one singleton Gson object instead of calling new Gson() all over the place?

Tags:

android

gson

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?

like image 336
treesAreEverywhere Avatar asked Oct 02 '15 11:10

treesAreEverywhere


1 Answers

I think both variants are OK, but if you create new Gson instances too often, it's not very good because of

  • your application consume more memory
  • creating the new object is not a cheap operation
  • potentially you can have compatibility issues when creating Gson instances with custom params in different places

As 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);
    }

}
like image 139
Igor Tyulkanov Avatar answered Oct 23 '22 17:10

Igor Tyulkanov