JavaFx2 or ScalaFx + Akka

巧了我就是萌 提交于 2019-12-03 05:42:43
  • Futures

The best way to use scala Futures together with a single-threaded toolkit such as JavaFX would be to define an executor that allows you to execute futures or actors on the thread of the UI Toolkit.

The same problem exists for Swing, which also requires updates to happen on the swing thread. Viktor Klang came up with the following solution Swing Execution Context. Here it is translated for JavaFX:

import akka.dispatch.ExecutionContext
import javafx.application.Platform
import java.util.concurrent.Executor

//
object JavaFXExecutionContext {
  implicit val javaFxExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(new Executor {
  def execute(command: Runnable): Unit = Platform.runLater(command)
  })
}

You would use it like this:

// import the default ec
import scala.concurrent.ExecutionContext.Implicits.global
// define the JavaFX ec so we can use it explicitly
val fxec = JavaFXExecutionContext.javaFxExecutionContext
future {
  // some asynchronous computation, running on the default
  // ForkJoin ExecutionContext because no ec is passed
  // explicitly
}.map(result => {
  // update JavaFX components from result
  // This will run in the JavaFX thread.
  // Check Platform.isFxApplicationThread() to be sure!
})(fxec)

The pipeline of futures can be very complex, as long as the step(s) that interact with JavaFX components are all running on the JavaFX ExecutionContext.

Note: it is up to you whether you make the ForkJoin ec the default and pass the JavaFX ec explicitly or vice versa. It might be a good idea to make the JavaFX ec the default to prevent errors and mark the parts that can run asynchronously explicitly with the ForkJoin ec.

  • Actors

For integrating an Actor based system with a single-threaded UI toolkit there is also a solution. See Swing Actors. All you have to do is to replace the

SwingUtilities.invokeLater(command)

with

Platform.runLater(command)

and you're good to go!

  • When to use which

If you have a big UI application and just want to spin off some asynchronous operations (loading a file or doing some computation), the futures-based approach is probably preferable. But be careful not to do any interaction (neither read nor write) with JavaFX components in the futures that run asynchronously on the default execution context.

If you have a large actor based system and just want to attach an UI to some parts, having a few actors that run on the JavaFX thread is probably preferable. YMMV.

There are a couple of things you need to consider when using multiple threads in JavaFX:

  • Any code that ends up touching the scene graph (e.g. by updating data that is bound to controls) must be wrapped in Platform.runLater. If you use the built-in multi-threading API in JavaFX (i.e. Task and Service) this will be done automatically, but if you use any other multi-threading utility you must do it yourself.

  • Your multi-threading utility (i.e. Akka) must (I think) somehow be told to leave some room for the JavaFX event thread. If you look at the source for Service you see that they take quite some care when configuring the executor. I'm not sure about the details of this, but when I experimented with using Scala's Futures with JavaFX, I observed some unresponsiveness in the UI when using the default ExecutionContext, that disappeared when I used a custom one based on the Service implementation.

There is no support in ScalaFX (or any other toolkit as far as I know) for working with either Scala Futures or Akka in a way that lets you forget about the two points above, but it would surely be interesting to look into.

mucaho

Here is the gist about akka actors that can be run on the Swing or JavaFX thread. It is a convenient, copy-pastable extension of Victor Klang's Swing Actors based on Rüdiger's answer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!