【问题标题】:Parsing tiny JSON from android assets is incredibly slow从 android 资产中解析微小的 JSON 非常慢
【发布时间】:2020-01-14 10:49:02
【问题描述】:

我正在用 Kotlin 编写一个简单的 Android 应用程序,它将向用户显示按类别划分的祈祷。 assets 文件夹中有 5 个 JSON 文件,每个文件只有 10 KiB 左右。

我使用 Klaxon 将 JSON 文件解析为这两个数据类:

data class Prayer(val prayerName: String, val verseTitle: String, val verseBody: String,
              val prayerLine: String, val prayerBody: String, val prayerEnding: String)

data class PrayerCategory(val title: String, val bgImage: String, val headerImage: String,
                      val prayers : List<Prayer>)

这是我用来解析祈祷的代码:

private fun loadPrayerNames(jsonFile: String) {
    val millis = measureTimeMillis {
        val input = assets.open("${jsonFile}.json")
        val prayerCategory = Klaxon().parse<PrayerCategory>(input)

        if (prayerCategory != null) {
            for (prayer in prayerCategory.prayers) {
                val prayerName = prayer.prayerName
                prayersMap[prayerName] = prayer
            }
        }
    }

    println("Loading prayer category took $millis ms.")
}

如您所见,只有 一个 访问资产。没有 assets.list(),没有废话。 正如你所注意到的,我已经测量了时间..让你猜猜..这是调试输出:

加载祈祷类别耗时 3427 毫秒。

是的,没错。加载和解析 10KiB 大 JSON 花了 3.5 秒! 我重复一遍。不涉及火箭科学。只需解析 10 KiB JSON。 3.5秒……嗯……

顺便说一句,我正在诺基亚 6.1 上测试它,这是一款非常活泼的手机。

所以..我的问题:

  1. 是什么导致了这种行为?
  2. 除了建立一个数据库来存储大约 50 个祈祷词之外,还有什么方法可以加快速度?

非常感谢您的帮助!

【问题讨论】:

  • @Drawn Raccoon 现在尝试了,它花了更长的时间:3472 ms :D ...不过这可能是 Klaxon 的问题。我发现了这个:github.com/cbeust/klaxon/issues/154.. 现在尝试使用 Moshi 作为替代方案,现在加载“仅”需要 1000 毫秒。
  • 您可以在模块的build.gradle 文件中的android 闭包中尝试aaptOptions { noCompress "json" },以禁用JSON 压缩,看看是否能提高性能。或者,将整个资产读入String,然后解析字符串,看看是否能提高性能。
  • @CommonsWare 感谢您的建议,但我已经使用不同的解决方案解决了它。我相信 Android 资产在这里是完全无辜的。 :-)

标签: android json kotlin klaxon


【解决方案1】:

在性能方面,Android 资产似乎名声不佳。但是,我的测试证明,在这种情况下,是 Klaxon 库负责。

在发现 Klaxon 中的主要性能问题(参见 https://github.com/cbeust/klaxon/issues/154)后仍未修复,我尝试了推荐的替代方案:Moshi (https://github.com/square/moshi)。

Moshi 确实提高了性能,但解​​析我的 JSON 仍然需要大约 1 秒。

在这些实验之后,我使用了老式的 JSONObject 解析:

data class Prayer(val prayerName: String, val verseTitle: String, val verseBody: String,
                val prayerLine: String, val prayerBody: String, val prayerEnding: String) {
    companion object {
        fun parseJson(json: JSONObject) : Prayer = Prayer(json.getString("prayerName"),
                json.getString("verseTitle"),  json.getString("verseBody"),
                json.getString("prayerLine"),  json.getString("prayerBody"),
                json.getString("prayerEnding"))

    }
}

data class PrayerCategory(val title: String, val bgImage: String, val headerImage: String,
                        val prayers : List<Prayer>) {
    companion object {
        fun parseJson(json: JSONObject): PrayerCategory {
            val prayers = ArrayList<Prayer>()
            val prayersArray = json.getJSONArray("prayers")

            for(i in 0 until prayersArray.length()) {
                prayers.add(Prayer.parseJson(prayersArray.getJSONObject(i)))
            }

            return PrayerCategory(json.getString("title"), json.getString("bgImage"),
                json.getString("headerImage"), prayers)
        }
    }
}

这已将解析时间从 3427 毫秒减少到 13 毫秒。结案。 ;-)

【讨论】:

  • 我怀疑真正的潜在问题在于如何使用流。使用JSONObject,您必须将整个JSON 读入String,然后从那里解析。我的猜测是,如果您为 Moshi(可能还有 Klaxon)这样做,您将获得类似的速度提升。话虽如此,如果您对JSONObject 解决方案感到满意,那么您的工作就完成了! :-)
  • @CommonsWare 我已经尝试过使用val input = assets.open("${jsonFile}.json").bufferedReader().readText() 作为输入,但它根本没有任何区别。但我也需要试试杰克逊——希望那个会更快。但无论如何,我现在不需要复杂的库,因为它是一个非常简单的项目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 2016-08-12
  • 2014-09-02
  • 2011-12-18
  • 2013-09-03
  • 2014-05-08
  • 2015-04-28
相关资源
最近更新 更多