Threads
A thread is a sequence of instructions. Most Android applications run on one single main thread called UI Thread. This thread will:
- πΌοΈ Render the View
- π§ Handle user interactions
- π Process events
- ...
It's responsible for Android Lifecycle, so it's the one executing methods such as onCreate
, onResume
and so on.
As this main thread is responsible for many things, we can't execute some code that takes too long, otherwise it will result in an ANR = Android Not Responding which basically means the app crashed.
So, when we need to run code that takes a long time, we either:
- Thread πͺ: we create a new thread for our task
- Coroutine π: we run the code in a coroutine
- WorkManager β³: we use the work manager
Asynchronous functions
A synchronous function is a (normal) function that returns the result when the function call is done. For instance:
fun addNumbers(a: Int, b: Int): Int {
return a + b
}
val result = addNumbers(5, 3)
But, it's not always possible. For instance, when requesting a resource from an API, it might take a few seconds for us to get a result.
There are two "solutions" that can be found:
- πͺ using what we call "callbacks"
- π« using asynchronous functions
suspend
Kotlin uses the keyword suspend
to declare asynchronous functions.
private suspend fun waitFiveSeconds() {
// delay 5000 = wait 5s
// delay is also a suspend fun
delay(5000)
}
β οΈ You can only call a suspend
function from another suspend
function.
Coroutines π
A coroutine can be used to run suspend functions.
Each coroutine is created from a scope.
- GlobalScope: as long as the app is running
- CoroutineScope: custom scope
- lifecycleScope: inside an Activity/Fragment
- viewModelScope: only available inside a ViewModel
And, on each, you can run one of these functions
- launch {}: simply run a coroutine
- runBlocking {}: block the running thread
- async {}: generate a Deferred that we can "await"
You can add a scope, for instance, launch(SCOPE) {}.
- Dispatchers.Main: use the Main Thread
- Dispatchers.IO: use the IO thread
- Dispatchers.Default: default for intensive tasks
- Dispatchers.Unconfined
Ex: Inside a ViewModel
class XXXViewModel : ViewModel() {
init {
viewModelScope.launch {
waitFiveSeconds()
}
}
}
Ex: Inside an Activity/Fragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
waitFiveSeconds()
}
}
}
Ex: Using CoroutineScope
CoroutineScope(Job()).launch {
// ...
}
Lifecycle-aware coroutines
See Lifecycle-aware coroutines
Ex: automatically started/restarted when the application is STARTED.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// ...
}
}
Repeat a task every XXX seconds
See also the lifecycle-aware coroutines.
while (true) {
// your task here
// wait, and loop again in 60 seconds
delay(60000)
}
π» To-do π»
Stuff that I found, but never read/used yet.