Json海量数据解析

Json海量数据解析

前言

​ 在android开发中,app和服务器进行数据传输时大多数会用到json。在解析json中通常会用到以下几种主流的解析库:jackson、gson、fastjson。而对于从server端获取的数据量很小时候,我们可能会忽略解析所产生的性能问题。而我在开发的过程中就碰到因为解析json而产生严重的问题。

问题场景

先描述以下问题的场景:app做收银库存管理。这时候每次登陆时候会去服务端同步所有的商品、分类等数据。而这时候,当商品的数量很大的时候,客户端拿到数据时候对app来说还是比较大的。而server端是将所有的数据序列化为json字符串存入到文件,然后app去下载文件并进行解析。下面说下我的修改历程。

踩坑过程

  • 第一版代码是直接讲文件读出为字符串,使用gson直接反序列化 new Gson().fromJson(String s,Type type)这时候OOM,查看日志,发现文件读出字符串时候直接OOM了(当初并没有考虑会有这么大的数据,晕倒)。从server端下载下来的文件就有20M左右。
  • 第二版代码使用FastJson的JSONReader。对每个对象进行单独序列化。也就是下面讲到的fastjson方法1。这时候OOM问题的解决了。因为是读的文件流,边读边解析数据。基本解决了问题。但通过Android Studio的Monitors发现,解析时候内存不断的在被消耗(汗。。还好没有爆掉)。
  • 第三版代码使用Fastjson的JSONReader。对每个json的每个key每个value都单独的解析和读取。也就是下面讲到的fastjson方法2。这时候所有的性能问题全部解决,速度最快,几乎没有消耗多少内存。

​ 上面是我一步步走过得坑,唉。可能对于看过fastjson源码的童鞋来说so easy。但第一次碰到后,坑还是得一步步的踩。当然也是要不断的通过看源码、写测试代码、比较内存和时间。下面是我做的一些测试。

测试验证

准备工作
  • 相关依赖库

        compile group: 'com.alibaba', name: 'fastjson', version: '1.2.29'
        // https://mvnrepository.com/artifact/commons-io/commons-io
        compile group: 'commons-io', name: 'commons-io', version: '2.5'
        // https://mvnrepository.com/artifact/com.google.code.gson/gson
        compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0's
    
  • 测试数据生成

        public void createDataFile() {
            List<Good> goodList = new ArrayList<>();
            for (int i = 0; i < 200000; i++) {
                Good good = new Good(System.currentTimeMillis() + "_" + i,
                        new String("booke") + i, 10.f + i,
                        System.currentTimeMillis() + "",
                        "describe book" + i,
                        i);
                goodList.add(good);
            }
            try {
                String json = JSONArray.toJSONString(goodList);
                FileUtils.write(new File("e://goods.json"), json, "UTF-8");
            } catch (IOException e) {
                log("" + e.getMessage());
                e.printStackTrace();
            }
        }
    
结果分析
  • gson解析

    使用流进行读取。20W条数据,内存不断的被消耗。两次解析时间为 50,488ms、48,940ms 性能是相当的差

List<Good> list = new Gson().fromJson(new InputStreamReader(getAssets().open("goods.json"),     
            "UTF-8"), new TypeToken<List<Good>>() {}.getType());
1.png
  • fastjson方法1

使用流进行读取。内存也是不断被消耗。三次解析时间为 33,394ms 31,632ms 32,378ms

JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"),
            "UTF-8"));
reader.startArray();
while (reader.hasNext()) {
    Good good = reader.readObject(Good.class);
}
reader.endArray();
reader.close();
reader = null;
2.png
  • fastjson方法2

    使用流进行读取,每个key和value自己来处理。三次解析时间为 31,242ms 31,583ms 30,834ms。同时,内存几乎没有太多的占用,比较的平稳。这个方法当然最优。

JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"),
           "UTF-8"));
reader.startArray();
while (reader.hasNext()) {
    reader.startObject();
    Good good = new Good();
    while (reader.hasNext()) {
        String key = reader.readString();
        if ("id".equals(key)) {
                good.setId(reader.readString());
            } else if ("name".equals(key)) {
                good.setName(reader.readString());
            } else if ("price".equals(key)) {
                good.setPrice(Double.parseDouble(reader.readString()));
            } else if ("barCode".equals(key)) {
                 good.setBarCode(reader.readString());
            } else if ("desc".equals(key)) {
                 good.setDesc(reader.readString());
            } else if ("count".equals(key)) {
                 good.setCount(Integer.parseInt(reader.readString()));
            } else {
                 reader.readObject();
            }   
        }
    reader.endObject();
    }
reader.endArray();
reader.close();
reader = null;
3.png

最后我们对比消耗时间

5.png

其他

  • Good.java
public class Good {
  private String id;
  private String name;
  private double price;
  private String barCode;
  private String desc;
  private int count;
  public Good() {
  }
  public Good(String id, String name, double price, String barCode, String desc, int count) {
      this.id = id;
      this.name = name;
      this.price = price;
      this.barCode = barCode;
      this.desc = desc;
      this.count = count;
  }
  public String getId() {
      return id;
  }
  public void setId(String id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public double getPrice() {
      return price;
  }
  public void setPrice(double price) {
      this.price = price;
  }
  public String getBarCode() {
      return barCode;
  }
  public void setBarCode(String barCode) {
      this.barCode = barCode;
  }
  public String getDesc() {
      return desc;
  }
  public void setDesc(String desc) {
      this.desc = desc;
  }
  public int getCount() {
      return count;
  }
  public void setCount(int count) {
      this.count = count;
  }
  @Override
  public String toString() {
      return "Good{" +
              "id='" + id + '\'' +
              ", name='" + name + '\'' +
              ", price=" + price +
              ", barCode='" + barCode + '\'' +
              ", desc='" + desc + '\'' +
              ", count=" + count +
              '}';
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容