Hey guys. I'm going through many different article...
# android
d
Hey guys. I'm going through many different articles and tutorials but I'm getting confused and can't figure out how to return a value from an http call in an onCreate method.
m
Do you mean “how to block onCreate() from continue executing util you get a HTTP response from somewhere” ?
d
I'm trying to do something like this.
Where getStudents() is making an httpcall (I'm using okhttp). However, I always end-up blocking the main thread and showLoadingAnimation never finishes before getStudents()
m
Try this, it will be closer to what you want
Copy code
GlobalScope.launch(Dispatchers.Main) { 
            val students = withContext(<http://Dispatchers.IO|Dispatchers.IO>) { getStudents() }
            recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
            recyclerView.adapter = MainAdapter(students)
            hideLoadingAnimation()
        }
but you should really also use a proper
CoroutineScope
for the coroutine, but let’s handle one issue at a time.
👍 1
d
Oh nice, it looks much better now. The loading animation showed up, but the app crashed with the error android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. on this line recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
Maybe I have to run it from runOnUiThread
Aight 🙂 Got it! Perfect.
g
No, please do not use runOnUiThread, you alredy use coroutines, they already provide way to run anything on any thread. You just should call
hideLoadingAnimation
from coroutine with Main dispathcer
also, do not use GlobalScope, it’s very bad idea, your activity will leak Instead use lifecycleScope extension provided by Android KTS, it gives you scope that already attached to Activity lifecycle and uses Main dispatcher by default. See https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope In general you need something like this:
Copy code
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.student_list)
    
    showLoadingAnimation()
    lifecycleScope.launch {
        val students = getStudents()
        recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
        recyclerView.adapter = MainAdapter(students)
        hideLoadingAnimation()
    }
}
withContext(Dispatchers.IO) { getStudents() }
It’s not good style. Depends on how
getStudents()
implemented, if it’s blocking, better to make in suspend function, also if it’s network request no need to use IO dispatcher, better use some asyncronous http client
Also, I recommend to read official guide about UI programming with coroutines: https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md
m
@gildor
It’s not good style.
If getStudents() is a “classical” Thread-blocking function call (which I assumed was the case here) it’s fine though, isn’t it? But yes even better if it would be a
suspend
function of course so there was no need for
withContext
g
If getStudents() is a “classical” Thread-blocking function call it’s fine though, isn’t it?
In very general case it’s true, but in most cases it’s just some network request that doesn’t require blocking the thread And even so, and you control function better rename it to
getStudentsBlocking()
And even if you do not control it, better to abstract this function to some suspend wrapper before use it in your UI
c
There should be a general understanding that any suspend function should configure itself to run on the appropriate dispatcher, so you do not need to worry about it as the consumer of a suspend function. Thus, your HTTP client, if it provides suspending functions, should already be running on the IO dispatcher internally. If you’re creating the suspending wrapper functions yourself, then put the
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
block inside the HTTP call suspend wrapper, so that using it from your activity does not need to put it there
🤝 1