Search code examples
androidasynchronouskotlinanko

Anko's uiThread sporadically not fired


I am currently using Anko's doAsync to handle asyncronous tasks. Specifically, i'm using it to parse a response json to an ArrayList of data objects, as seen below:

// Asynchronously parse server response
doAsync {
    val itemlist = parseResponse(response)
    uiThread {
        Otto.INSTANCE.post(UpdateRedeemedVoucherViewsEvent(itemlist))
    }
}

Most of the time, like on an Google Pixel (Android 8.0.0), this works just fine. My objects get parsed, and the Otto bus event will be posten on the ui thread.

But in some rare cases, this process fails. So far i noticed that the ui thread call wont be fired on an Galaxy S5 Mini (Android 5.1.1). Although my data was parsed without any errors or exceptions, the code snippet in the uiThread brackets won't be executed.

So far i was not able to determine any reason for this behaviour. No logcat logs, nothing. There is no scheme of when this error will appear.

Has anybody had the same or a similar problem with kotlin and/or Anko?

UPDATE: I've found a solution by myself, please see my answer below for an explanation.


Solution

  • Thank you for your help guys. I've figured it out by myself, so i'm posting this answer just in case anyone else faces this problem.

    TL;DR: the context gets lost, so the uiThread won't be executed

    Solution: I've stumbled across this post where an important feature of the uiThread method was mentioned:

    Basically you have an async function that will execute it’s code in another thread, and will give the chance of returning main thread using uiThread. async is an extension function implemented for Context, and will use a weak reference of it, so it won’t prevent GC from releasing its memory.

    Upon further investigation, i've found the following log right after my parsing was done, and where the uiThread should have been called:

    I/art: Background partial concurrent mark sweep GC freed 30879(16MB) AllocSpace objects, 16(2MB) LOS objects, 40% free, 16MB/27MB, paused 2.123ms total 196.372ms
    

    So it seems that there is indeed some GC action, which may or may not the context to get freed.

    To prevent this, i'm using Ankos runOnUiThread, which can be called on a given context reference:

    // Asynchronously parse server response
    doAsync {
        val itemlist = parseResponse(response)
        context.runOnUiThread {
            Otto.INSTANCE.post(UpdateRedeemedVoucherViewsEvent(itemlist))
        }
    }
    

    As far as i can tell by now, this seems to have done the trick.