androidjsonkotlinklaxon

Parsing tiny JSON from android assets is incredibly slow


I am writing a simple Android app in Kotlin, which will show prayers divided into categories to user. There are 5 JSON files in assets folder, each of them has just around 10 KiB.

I use Klaxon for parsing the JSON files into those two data classes:

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>)

Here is the code I use for parsing the prayers:

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.")
}

As you can see, there is just one access to assets. No assets.list(), no bullshit. And as you noticed, I have measured the time.. make your guesses.. Here is the debug output:

Loading prayer category took 3427 ms.

Yup, that's right. Loading and parsing 10KiB big JSON took 3.5 SECONDS! I repeat. No rocket science involved. Just parsing 10 KiB JSON. 3.5 seconds..... Hmm..

Btw, I am testing it on Nokia 6.1, which is a pretty snappy phone.

So.. my questions:

  1. What causes this behaviour?
  2. Is there any way how to speed this up other than building a database for storing about 50 prayers?

I will be very thankful for your help!


Solution

  • Android assets seem to have bad reputation when it comes to performance. However, my testing proved that in this situation, it was Klaxon library who was responsible.

    After finding major performance problem in Klaxon (see https://github.com/cbeust/klaxon/issues/154), which is still not fixed, I have tried recommended alternative: Moshi (https://github.com/square/moshi).

    Moshi did improve performance, but parsing my JSON still took about 1 second.

    After these experiments, I have resorted to the good old fashioned parsing using 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)
            }
        }
    }
    

    This has reduced parsing time from 3427 ms to 13ms. Case closed. ;-)