LittleKt supports the use of coroutines and asynchronous operations.
Coroutines
The Context
is the default scope that should be used instead of GlobalScope
in a LittleKt application. We can also use the KtScope
object which points to the same CoroutineContext
. Sometimes it might be easier to switch using KtScope
vs trying to a Context
reference.
LittleKt provides two main implementations of coroutine dispatchers:
RenderingThreadDispatcher
: executes tasks on the main rendering thread. Available viaDisptachers.KT
. This is the default dispatcher used by theContext
/KtScope
internally.AsyncThreadDispatcher
: wraps anAsyncExecutor
to execute tasks.newSingleThreadAsyncContext()
: factory method that creates anAsyncExecutor
with a single thread.newAsyncContext(numThreads)
: factory method that creates anAsyncExecutor
with the specified amount of threads.AsyncThreadDispatcher
: class that allows to wrap an existingAsyncExecutor
. Ensure that thethreads
property is set to the correct number of threads.
LittleKt also provides a few utility methods:
onRenderingThread
: suspends the coroutine to execute a task on the main rendering thread and return its result. Should be used if you dispatch a corutine with a non-rendering thread dispatcher and need to execute a task on the main rendering thread, such as preparing aTexture
.isOnRenderingThread
: checks if the corutine is being executed on the main rendering thread.
Usage
Start a coroutine on the main rendering thread:
suspend fun Context.doWork() {
launch { // can also call context.launch directly.
logger.info { "A coroutine from the render thread!" }
}
}
suspend fun Context.doWork() {
KtScope.launch { // the same thing as context.launch
logger.info { "A coroutine from the render thread!" }
}
}
Performing asynchronous work outside the rendering thread:
fun doAsyncWork() {
val executor = newSingleThreadAsyncContext()
KtScope.launch {
logger.info { "On render thread" }
withContext(executor) {
logger.info { "On async thread" }
}
logger.info { "Back on render thread" }
}
}
Switching threads:
private val job = Job()
private val scope = CoroutineScope(job)
fun switchThreads() {
scope.launch {
logger.info { "On scope context!" }
withContext(Dispatchers.KT) {
logger.info { "On render thread" }
}
logger.info { "Back on scope context!" }
onRenderingThread {
logger.info { "Back on render thread"}
}
}
}
We can even pass data to the rendering thread from another thread using Context.postRunnable()
. This will run the runnable in the rendering thread on the next frame before render()
is called:
fun postRunnableWork() {
val executor = newSingleThreadAsyncContext()
KtScope.launch(executor) {
logger.info { "On async thread" }
context.postRunnable {
logger.info { "I will run on the render thread next frame!" }
}
logger.info { "Still on async thread" }
}
}