Как отправить сопрограммы непосредственно в главный поток на JVM?
Я устанавливаю основанную на kotlin сопрограмму сетевую среду для jvm. Классы Client и Server реализуют CoroutineScope, а переопределение для coroutinecontext - это Dispatchers.IO, так как я уверен, что это правильный Dispatcher для использования в таком случае. Однако я хочу обработать прочитанные пакеты в основном потоке или, по крайней мере, предоставить эту опцию. Не читая документацию, я использовал Dispatchers.Main, который теперь я понял, для потока пользовательского интерфейса Android. Есть ли диспетчер, который я могу использовать для запуска сопрограммы в главном потоке? Если нет, как бы я сделал один?
Я просмотрел документацию kotlin о том, как создать диспетчер, основанный на одном потоке, но я не смог найти ничего, кроме newSingleThreadContext, который создает новый поток. Я также выяснил, что возможно создать диспетчер из Java-исполнителя, но я все еще не уверен, как ограничить это уже существующим потоком.
class AbstractNetworkComponent : CoroutineScope {
private val packetProcessor = PacketProcessor()
private val job = Job()
override val coroutineContext = job + Dispatchers.IO
}
class PacketProcessor : CoroutineScope {
private val job = Job()
override val coroutineContext = job + Dispatchers.Main //Android only!
private val packetHandlers = mutableMapOf<Opcode, PacketHandlerFunc>()
fun handlePacket(opcode: Opcode, packet: ReceivablePacket, networker: Writable) {
launch(coroutineContext) {
packetHandlers[opcode]?.invoke(packet, networker)
}
}
}
Так что с Dispatchers.Main я получаю исключение IllegalStateException из-за отсутствия компонента Android. Есть ли способ создать диспетчер, который блокирует основной поток до его завершения (как это делает runBlocking?) Спасибо!
2 ответа
runBlocking
это именно то, что вам нужно. Он создает диспетчер и устанавливает его в контексте сопрограммы. Вы можете получить доступ к диспетчеру с
coroutineContext[ContinuationInterceptor] as CoroutineDispatcher
а затем вы можете передать его объекту, который реализует CoroutineScope
или что еще вы хотите сделать с этим. Вот пример кода:
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlin.coroutines.ContinuationInterceptor
fun main() {
println("Top-level: current thread is ${Thread.currentThread().name}")
runBlocking {
val dispatcher = coroutineContext[ContinuationInterceptor]
as CoroutineDispatcher
ScopedObject(dispatcher).launchMe().join()
}
}
class ScopedObject(dispatcher: CoroutineDispatcher) : CoroutineScope {
override val coroutineContext = Job() + dispatcher
fun launchMe() = launch {
val result = withContext(IO) {
"amazing"
}
println("Launched coroutine: " +
"current thread is ${Thread.currentThread().name}, " +
"result is $result")
}
}
Это напечатает
Top-level: current thread is main
Launched coroutine: current thread is main, result is amazing
Согласно Руководству по программированию пользовательского интерфейса с сопрограммами, kotlinx.coroutines имеет три модуля, которые обеспечивают сопрограммный контекст для различных библиотек приложений пользовательского интерфейса:
- kotlinx-coroutines-android - Диспетчеры. Основной контекст для приложений Android.
- kotlinx-coroutines-javafx - контекст Dispatchers.JavaFx для приложений пользовательского интерфейса JavaFX.
- kotlinx-coroutines-swing - контекст Dispatchers.Swing для приложений пользовательского интерфейса Swing.
Кроме того, диспетчер пользовательского интерфейса доступен через Dispatchers.Main
из kotlinx-coroutines-core и соответствующей реализации (Android, JavaFx или Swing) обнаруживается с помощью API ServiceLoader. Например, если вы пишете приложение JavaFx, вы можете использовать либо Dispatchers.Main
или же Dispachers.JavaFx
расширение, это будет тот же объект.