kotlin - Kotlin CoroutineScope 不工作 - JavaFx(使用 TornadoFx)
问题描述
现在,我在 JavaFx(使用 TornadoFx)中使用 Kotlin Coroutine 时遇到问题。问题是CoroutineScope
在 Kotlininit
块 +launch
扩展之后我的不工作CoroutineContext
也不起作用。
- 我的视图抽象类。
abstract class View(
title: String? = null,
icon: Node? = null
) : tornadofx.View(title, icon), CoroutineHandler, Injectable, Cleanable {
val viewScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.JavaFx.immediate)
open val fragmentContainer: Pane? get() = null
abstract val viewModel: ViewModel
override fun launch(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(start = start, block = block)
override fun launchDefault(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(Dispatchers.Default, start, block)
override fun launchMain(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(Dispatchers.JavaFx, start, block)
override fun launchMainImmediate(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(Dispatchers.JavaFx.immediate, start, block)
override fun launchIO(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(Dispatchers.IO, start, block)
override fun launchUnconfined(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewScope.launch(Dispatchers.Unconfined, start, block)
override fun <T> Flow<T>.start() = launchIn(viewScope)
open fun openFragment(fragment: Fragment) {
with(fragmentContainer!!.children) {
if (isNotEmpty()) clear()
add(fragment)
}
}
override fun clean() {
viewScope.cancel()
viewModel.clean()
}
override fun onDock() {
clean()
super.onDock()
}
- 我的 ViewModel 抽象类
abstract class ViewModel : tornadofx.ViewModel(), CoroutineHandler, Cleanable {
val viewModelScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.JavaFx.immediate)
override fun launch(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(start = start, block = block)
override fun launchDefault(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(Dispatchers.Default, start, block)
override fun launchMain(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(Dispatchers.JavaFx, start, block)
override fun launchMainImmediate(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(Dispatchers.JavaFx.immediate, start, block)
override fun launchIO(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(Dispatchers.IO, start, block)
override fun launchUnconfined(start: CoroutineStart, block: suspend CoroutineScope.() -> Unit): Job =
viewModelScope.launch(Dispatchers.Unconfined, start, block)
override fun <T> Flow<T>.start() = launchIn(viewModelScope)
override fun clean() {
viewModelScope.cancel()
}
}
- 查看实施 (LoginView)。只需转到
init
块和controlsInitialization
方法。
class LoginView : View("Login") {
override val root: VBox by fxml()
private val usernameTextField: TextField by fxid()
private val passwordField: PasswordField by fxid()
private val submitButton: Button by fxid()
private val backButton: Button by fxid()
private val resultLabel: Label by fxid()
@Inject
override lateinit var viewModel: LoginViewModel
init {
viewScope.launch { println("LAUNCH VIEW") } //Printed
launchMain { println("MAIN VIEW") } //Not Printed
launchMainImmediate { println("MAIN IMMEDIATE VIEW") } //Printed
launchDefault {
println("DEFAULT VIEW 1") //Printed
appComponent.inject(this@LoginView)
controlsInitialization()
collectorsInitialization()
println("DEFAULT VIEW") //Not Printed
}
launchIO { println("IO VIEW") } //Printed
launchUnconfined { println("UNCONFINED VIEW") } //Printed
}
private fun collectorsInitialization() {
with(viewModel) {
getUsername().onEach {
with(usernameTextField) {
if (it != null) {
invisible()
passwordField.visible()
submitButton.text = "Login"
backButton.visible()
resultLabel.invisible()
submitButton.enable()
} else {
showResultLabel("Username salah.")
}
enable()
}
}.start()
isSuccessLogin().onEach {
if (it) {
MainView().openWindow(owner = null)
close()
} else showResultLabel("Password salah.")
}.start()
getLoginFailedCount().onEach {
passwordField.clear()
if (it == 5 || it == 7 || it >= 9) {
val text = "Anda gagal login sebanyak $it, " +
"silahkan tunggu selama "
val waitTime = WaitTime(waitTimeInMinute)
var now = LocalTime.now()
var lastSecond = now.second
launchDefault {
with(waitTime) {
while (isNotOverYet) {
if (lastSecond != now.second) {
if (second == 0) {
minute--
second = 59
} else second--
viewScope.launch {
resultLabel.text = "$text $waitTime : $second"
}
lastSecond = now.second
}
now = LocalTime.now()
}
}
waitTimeInMinute += 5
submitButton.enable()
resultLabel.invisible()
}
} else {
submitButton.enable()
}
}.start()
}
}
private fun controlsInitialization() {
with(backButton) {
setOnAction {
invisible()
passwordField.apply {
invisible()
clear()
}
usernameTextField.visible()
if (!resultLabel.text.contains("Anda")) resultLabel.invisible()
}
}
submitButton.setOnAction {
submitButton.disable()
if (usernameTextField.isVisible) {
with(usernameTextField) {
disable()
println("$text 1") //Printed
launchIO { "$text 2" } //Not Printed
viewModel.checkIsUserExist(text)
}
} else {
with(passwordField) {
disable()
launchIO {
viewModel.login(text)
}
}
}
}
}
private fun showResultLabel(text: String) {
with(resultLabel) {
this.text = text
visible()
}
}
}
- ViewModel 实现 (LoginViewModel)。只需转到
init
块和checkIsUserExist
方法。
class LoginViewModel @Inject constructor(
private val loginUseCase: LoginUseCase
) : ViewModel() {
private val username = MutableStateFlow<String?>(null)
private val isSuccessLogin = MutableStateFlow(false)
private val loginFailedCount = MutableStateFlow(0)
var waitTimeInMinute: Int = 5
init {
viewModelScope.launch { println("LAUNCH VIEW MODEL") } //Not Printed
launchMain { println("MAIN VIEW MODEL") } //Not Printed
launchMainImmediate { println("MAIN IMMEDIATE VIEW MODEL") } //Not Printed
launchDefault { println("DEFAULT VIEW MODEL") } //Printed
launchIO { println("IO VIEW MODEL") } //Printed
launchUnconfined { println("UNCONFINED VIEW MODEL") } //Printed
}
fun checkIsUserExist(username: String) {
println("$username AAAA") //Printed
launchIO {
println(username) //Not Printed
val isExist = loginUseCase.checkIsUserExist(username)
println(username) //Not Printed
println("isExist: $isExist") //Not Printed
with(this@LoginViewModel.username) {
if (isExist) {
emit(username)
} else {
emit(null)
with(loginFailedCount) { emit(value + 1) }
}
}
}
println("$username ZZZZ") //Printed
}
fun login(password: String) = launchIO {
if (username.value == null) {
isSuccessLogin.emit(false)
with(loginFailedCount) { emit(value + 1) }
} else {
val user = loginUseCase.login(username.value!!, password)
isSuccessLogin.value = if (user != null) {
CashierApplication.build(user)
true
} else {
with(loginFailedCount) { emit(value + 1) }
false
}
}
}
fun getUsername(): StateFlow<String?> = username
fun isSuccessLogin(): StateFlow<Boolean> = isSuccessLogin
fun getLoginFailedCount(): StateFlow<Int> = loginFailedCount
- 我的 CoroutineHandler 接口
interface CoroutineHandler {
fun <T> Flow<T>.start(): Job
fun launch(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun launchDefault(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun launchMain(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun launchMainImmediate(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun launchIO(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun launchUnconfined(
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
suspend fun <T> withDefault(block: suspend CoroutineScope.() -> T): T =
withContext(Dispatchers.Default, block)
suspend fun <T> withMain(block: suspend CoroutineScope.() -> T): T =
withContext(Dispatchers.Main, block)
suspend fun <T> withMainImmediate(block: suspend CoroutineScope.() -> T): T =
withContext(Dispatchers.Main.immediate, block)
suspend fun <T> withIO(block: suspend CoroutineScope.() -> T): T =
withContext(Dispatchers.IO, block)
suspend fun <T> withUnconfined(block: suspend CoroutineScope.() -> T): T =
withContext(Dispatchers.Unconfined, block)
}
解决方案
实际上,我是override
错误的方法。我需要的override
方法onUndock
不是onDock
。