Issue with Coroutines


#1

Hey all! I have a coroutines question that I’ve been struggling with. I am making a network request in the following way:

fun fetchCharacterData(): Deferred<CharacterGenerator.CharacterData> {
    return GlobalScope.async(Dispatchers.Default) {
        val apiData = URL(CHARACTER_DATA_ENDPOINT).readText()
        CharacterGenerator.fromApiData(apiData)
    }
}

Then, in my main activity, I do the following within onCreate():

generateButton.setOnClickListener {
    GlobalScope.launch(Dispatchers.Main){
        characterData = fetchCharacterData().await()
        displayCharacterData()
    }
}

However, I am getting an ANR whenever the button is pressed. I also get a very vague error message in logcat: FATAL EXCEPTION: main.

Would anyone have any insight as to what the issue could be?


#2

I don’t know the cause for you issue, however using Dispatchers.Default for I/O operations is really a bad idea.

Please consider another one: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/index.html

Moreover returni a deferred/future/promise is generally a bad design choice, consider to rewrite your code as example:

suspend fun fetchCharacterData(): CharacterGenerator.CharacterData = withContext(Dispatchers.IO) {
    val apiData = URL(CHARACTER_DATA_ENDPOINT).readText()
    CharacterGenerator.fromApiData(apiData)
}

#3

The failure seems to be caused by, or in conjunction with, code that you are not listing here.

Can you create an example project that just showcases the problem?


#4

@fvasco Thanks so much for the input. I’ve tried to do what you’ve recommended, with the following adjustment to my setOnClickListener():

    generateButton.setOnClickListener {
        GlobalScope.launch(Dispatchers.Main){
            characterData = fetchCharacterData()
            displayCharacterData()
        }
    }

To give you a better context as to why I was doing my original implementation, I was following the coroutines chapter from Kotlin Programming from The Big Nerd Ranch. However, their implementation is based on kotlinx-coroutines-android:0.22.5, which is pretty outdated by this point. With that in mind, I’ve been trying to figure out why this isn’t working on my own.

Unfortunately, I’ve tried what you’ve recommended, with my slight adjustment to setOnClickListener(), but I’m still getting an ANR…

@andreartus I agree with your recommendation. I would normally consider creating a SSCCE for this. However, this project is quite simple for the most part… Feel free to take a look at my repo if that’d be helpful at all:


#5

I don’t know whether you have changed anything, but it seems to run fine by me.

I just cloned the repo, built and ran it. When I click I get a new character.

I’ll continue with this on your repo.


#6

Yes, it appears to be working fine now, thanks so much for looking into this for me. I made the following change which seems to have solved the issue:

suspend fun fetchCharacterData(): CharacterGenerator.CharacterData = withContext(Dispatchers.Default){
    val apiData = withContext(Dispatchers.IO) { URL(CHARACTER_DATA_ENDPOINT).readText() }
    CharacterGenerator.fromApiData(apiData)
}

…And then make the network call within the OnClickListener of the button as follows:

generateButton.setOnClickListener {
    launch {
        characterData = fetchCharacterData()
        displayCharacterData()
    }
}

Not sure what the issue was with the original implementation… But it appears to be working now. Thank you for all of your help on this! :smiley::pray::pray: