【问题标题】:Parse JSON on the flight Android在 Android 航班上解析 JSON
【发布时间】:2017-05-05 14:40:36
【问题描述】:

我正在使用 google GSON API 为我的 Android 项目解析 JSON 文件,但我遇到了性能问题。

这里是我使用谷歌 GSON API 解析 JSON 的源代码:

public void loadJsonInDb(String path){
    InputStream isJson = context.getAssets().open(path);
    if (isJson != null) {
        int sizeJson = isJson.available();
        byte[] bufferJson = new byte[sizeJson];
        isJson.read(bufferJson);
        isJson.close();
        String jsonStr = new String(bufferJson, "UTF-8");

        JsonParser parser = new JsonParser();
        JsonObject object = parser.parse(jsonStr).getAsJsonObject();
        JsonArray array = object.getAsJsonArray("datas");

        Gson gson = new Gson();
        for(JsonElement jsonElement : array){
            MyEntity entity = gson.fromJson(jsonElement, MyEntity.class);
            // Do insert into Db stuffs
        }
    }
}

这样做的问题是,在解析后我必须通过 JsonArray 使用 for 循环并执行所需的操作(这是在 SQLite DB 中插入数组中每个元素的 ORMLite),我想知道如果可以在解析期间在航班上执行插入,而不是等待计算数组。我在文档中看到,也许JsonStreamParser 可以完成这项工作,但我不知道如何使用它。

【问题讨论】:

  • 可能数据库插入是一个瓶颈。您使用内容提供程序还是直接插入?你使用 beginTransaction() endTRansaction() 吗?
  • Indead DB 插入是瓶颈之一,实际上我测量了处理时间。 JSON 解析:17 秒,数据库插入:50 秒。这就是为什么我想在解析过程中在飞行中插入数据到数据库中,这将是一种并行处理(解析+数据库插入)。我不使用 beginTransaction 或 endTRansaction,它会提高性能吗?
  • 我有类似的问题。约 1000 条记录的 Json 数组。如果使用内容提供商插入时间 ~ 1 分钟。如果直接访问 db(没有内容提供者)并在单个事务中插入大约需要 10 秒。

标签: java android json


【解决方案1】:

我有一些关于使用 Gson 和其他东西的注意事项。

  • 您应该关闭finally 块中的I/O 资源,以确保您没有资源泄漏(availableread 可能会引发异常,从而阻止资源成为closed)。 (另外我不确定在这里使用available 是否是个好主意。)
  • 在这种情况下,您不必使用Strings。对于这种情况,字符串通常是性能/内存杀手(很大程度上取决于它们的结果大小),因为字符串会累积到内存中,因此您会失去将所有内容首先收集到内存中的即时想法。在最坏的情况下,它可以使用OutOfMemoryError 完成您的应用程序。
  • 您可以读取具有指定编码的输入流,因此不需要字符串缓冲。
  • JsonParser 旨在返回 JSON 树:JsonElement 包含内存中的整个 JSON 树。听起来类似于上面的字符串案例,对吧?这里又是一个性能损失。
  • 创建Gson 实例可能有点昂贵(当然取决于如何比较),并且您可以实例化一次:它是线程安全的。
  • JsonStreamParser 也不是一个选项,因为每个 next() 都会在内存中生成另一个 JSON 树分支(同样,取决于您的 JSON 文档及其 $.datas 数组及其元素的大小)。
  • Gson.fromJson 使用查找来查找最佳类型适配器,并且您向 Gson 实例询问类型适配器一次,然后不再浪费时间进行查找。类型适配器通常也是完全线程安全的,因此可以被缓存。

总结以上内容,可以如下实现:

private static final Gson gson = new Gson();
private static final TypeAdapter<MyEntity> myEntityTypeAdapter = gson.getAdapter(MyEntity.class);
private static void loadJsonInDb(final String path)
        throws IOException {
    // Java 7 language features can be easily converted to Java 6 try/finally
    // Note the way how you can decorate (wrap) everything: an input stream (byte streams) to a reader (character streams, UTF-8 here) to a JSON reader (more high-level character reader)
    try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(context.getAssets().open(path), "UTF-8")) ) {
        // Ensure that we're about to open the root object
        jsonReader.beginObject();
        // And iterate each object property
        while ( jsonReader.hasNext() ) {
            // And check it's name
            final String name = jsonReader.nextName();
            // Another Java 7 language feature
            switch ( name ) {
            // Is it datas?
            case "datas":
                // The consume it's opening array token
                jsonReader.beginArray();
                // And iterate each array element
                while ( jsonReader.hasNext() ) {
                    // Read the current value as an MyEntity instance
                    final MyEntity myEntity = myEntityTypeAdapter.read(jsonReader);
                    // Now do what you want here
                }
                // "Close" the array
                jsonReader.endArray();
                break;
            default:
                // If it's something other than "datas" -- just skip the entire value -- Gson will do it efficiently (I hope, not sure)
                jsonReader.skipValue();
                break;
            }
        }
        // "Close" the object
        jsonReader.endObject();
    }
}

简单来说,您只需要编写一个解析器来使用每个令牌。现在,拥有以下 JSON 文档:

{
    "object": {
    },
    "number": 2,
    "array": [
    ],
    "datas": [
        {
            "k": "v1"
        },
        {
            "k": "v2"
        },
        {
            "k": "v3"
        }
    ]
}

上面的解析器将提取$.datas.* 只消耗尽可能少的资源。用System.out.println(myEntity.k); 替换// Now do what you want here 会产生:

v1
v2
v3

假设MyEntityfinal class MyEntity{final String k=null;}。请注意,您也可以使用这种方法处理无限的 JSON 文档。

【讨论】:

  • 感谢这次更新,我要试试。但是,您能告诉我对应的 myEntityTypeAdapter 是什么吗?
  • @Mouss 对不起,我忘了附上。请查看编辑。
【解决方案2】:

我有两个建议:

  1. 在 3 行中实现整个集合:

        Gson gson = new Gson();
        Type listType = new TypeToken<ArrayList<MyEntity>>(){}.getType();
        List<MyEntity> listOf = gson.fromJson(jsonStr, listType);
    
  2. 当您获得完整的实体列表时,请使用 bulkInsert 进行单笔交易。 There you can get the idea how do use it

附: 要使用bulkInsert,您必须从您的实体创建ContentValues 列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多