Gson在 Android开发中可能存在的陷阱

引言

本文来自于Foursquare的团队技术博客。Foursquare团队最近发现自家的Android app在使用过程中并没有传说当中的“如黄油般顺滑”,而且通过查看logcat发现app会频繁地发起 GC_FOR_ALLOC 调用,Activity和Fragment之间的跳转也没有想象当中那样快。于是好学的工程师们开始去挖掘这个背后的内容。

一切都是因为JSON解析的问题

通过初步的性能测试,我们发现以上现象的很大部分原因来自于app在解析JSON的时候耗时过多。于是我们决定深入去看看app的JSON解析代码有些什么问题?

Foursquare的Android应用都是通过一个 JSON API来同服务器进行交互的,JSON的解析则是使用Google的 Gson 库,也就是说使用Gson来反序列化JSON字符串生成相应的Java对象供Android开发者进行下一步处理。我们一般是这样使用Gson将一个描述venue(场地)的字符串转化成Java对象的:

String venueJson = "{"name": "Starbucks" ,"city": "New York"}”;
Gson gson = new Gson();
Venue venue = gson.fromJson(venueJson, Venue.class);
 
// Do something with object.
String name = venue.getName();

程序是这样没问题,可是实际上我们只需要venue的Name属性,但是上面的程序却解析了整个JSON字符串。是不是这里造成了一部分性能问题?如果我们只解析我们想要的属性,会不会降低JSON解析的时间?于是他们开始使用 Gson streaming API ,确保Android在解析JSON的时候省去了将Reader转化为String的步骤,直接解析Reader,示例代码如下:

我们从上面的代码可以发现 JsonReader stream 被直接传入 read() 方法而不是传入一个 JsonElement 树。通过上面的改进之后,即把我们自定义的 deserializer 改为继承 TypeAdapters 和 TypeAdapterFactorys ,我们app在解析大的响应的时候解析时间减少了 50% 以上。更重要的是,整个 app 感觉比以前要快多了app的滑动也顺畅多了。

一些感悟

  • 尽量使用 GSON 的 streaming APIs,特别是在像Android这样的内存制约较多的平台上。 The memory savings for non-trivial JSON strings are significant.
  • 使用 TypeAdapters 实现的deserializer 代码肯定要比使用 JsonDeserializers 实现的deserializer 代码要丑,因为TypeAdapters 比起JsonDeserializers 更偏向于底层了,或者说JsonDeserializers 要比TypeAdapters 更抽象一下,而越是底层的代码越具体。相应的JsonDeserializers 肯定也要比TypeAdapters 方案要灵活些。
  • 尽管存在这些缺点,但是很多时候代码的美观程度和灵活性相对于内存的使用,优先级还是要排在后面的
  • 尽可能少用自定义的 deserialization ,因为代码的复杂度增加了。

《Gson在 Android开发中可能存在的陷阱》有4个想法

  1. 正确的优化方案不应该是让API不要反回无关字段么?这样不光减少解析时间,还能加快网络响应速度….
    虽然 stream-> JsonElement -> Type 也是多走了一个步骤。。

    1. 那你的API得有多复杂啊?比如说你返回name对应一个api,返回address对应一个api?那还不如直接返回一个对象的json呢,就像文中的那样返回一个对象,然后再对这个对象操作。这是我的理解。

  2. “于是他们开始使用 Gson streaming API ,确保Android在解析JSON的时候每次只解析自己需要的属性”

    public T fromJson(Reader json,Class classOfT)
    这个API没有文中说所的功能吧?还是需要把整个Venue都解析出来的吧?

    1. 这里应该是我理解有误。多谢提醒。经过查询,我现在的理解就是本身之前的fromJson(String str,Class classOfT ) 里面的str就是用Reader里面转换过来的。所以Foursquare的工程师先以为省去将Reader转为string的过程可以节省系统开销,所以直接就使用fromJson(Reader json,Class classOfT )这个方法。后来他们才发现着瓶颈不在这里。
      再次感谢你的提醒。

发表评论

电子邮件地址不会被公开。 必填项已用*标注