首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JavaFX 2:背景和Platform.runLater与任务/服务

JavaFX 2:背景和Platform.runLater与任务/服务
EN

Stack Overflow用户
提问于 2014-08-10 00:12:31
回答 1查看 8.7K关注 0票数 6

我对Task/Service在JavaFX中的概念非常困惑。

我在后台工作中使用了一个基于后台线程的模型,它调用Platform.runLater来更新UI。

假设我对进度条什么的不感兴趣。我正在对我的模型做一些必须在GUI视图中更新的实际工作(例如,根据背景中的某种连接随时间更新的参与者列表,基于某些用户输入的参与者列表,按年龄和来源分类)。这就是我通常使用启动的后台线程实现的,并且在其中使用Platform.runLater

现在,在JavaFX 2中,它们使用Tasks和Services实现了所有这些并发性,这表明使用它们更好。但我看不出有什么例子能达到我所说的目的。

通过绑定一些属性来更新进度条很好(但这些是关于任务的信息,而不是您的模型)。

那么,我如何能够根据我的模型实际更新我的视图的内容呢?我应该从Platform.runLater内部打电话给Task吗?若否,机制是甚麽?当任务成功时如何捕获并获得结果(实际模型的更新)来更新视图?

不幸的是,Oracle的教程在这方面不是很好。给我指点一些好的教程也会有帮助。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-10 02:19:38

TaskService类的设计是为了鼓励在GUI编程中某些(但不是所有)常见场景中的良好实践和正确使用并发性。

一个典型的场景是,应用程序需要执行一些逻辑来响应可能需要很长时间的用户操作(可能是长时间的计算,或者更常见的是数据库查找)。该过程将返回一个结果,然后用于更新UI。如您所知,长时间运行的进程需要在后台线程上执行,以保持UI响应,而对UI的更新必须在FX应用程序线程上执行。

Task类为这种功能提供了一个抽象,并表示一个执行并产生结果的“一次性”任务。call()方法将在后台线程上执行,并被设计为返回进程的结果,并且在FX线程上通知任务完成时会有事件侦听器。强烈鼓励开发人员以不可变状态初始化Task实现,并让call()方法返回一个不可变对象,这将确保后台线程和之间的适当同步。

对于这些类型的任务,还有其他常见的要求,例如,随着任务的进展更新消息或进度。应用程序还可能需要监视类的生命周期状态(等待运行、运行、完成、异常失败等)。正确地编程是相当困难的,因为它必然涉及访问两个不同线程中的可变状态,并且有许多应用程序开发人员不知道这些微妙之处。Task类为这种功能提供了简单的挂钩,并负责所有的同步工作。

要使用此功能,只需创建一个Task,其call()方法返回计算结果,为状态从RUNNINGSUCCEEDED的转换注册一个处理程序,并在后台线程中运行该任务:

代码语言:javascript
复制
final Task<MyDataType> task = new Task<MyDataType>() {
    @Override
    public MyDataType call() throws Exception {
        // do work here...
        return result ;
    }
};

task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
    @Override
    public void handle(WorkerStateEvent event) {
        MyDataType result = task.getValue(); // result of computation
        // update UI with result
    }
});

Thread t = new Thread(task);
t.setDaemon(true); // thread will not prevent application shutdown
t.start();

幕后的工作方式是Task维护一个state属性,该属性是使用常规的JavaFX ObjectProperty实现的。Task本身封装在Callable的私有实现中,Callable实现是传递给超类构造函数的对象。因此,Callablecall()方法实际上是在后台线程中执行的方法。Callablecall()方法实现如下:

  1. 调度对FX应用程序线程(即使用Platform.runLater())的调用,该线程首先将state更新为SCHEDULED,然后更新为RUNNING
  2. 调用TaskTask方法(即用户开发的call()方法)
  3. 调度对FX应用程序线程的调用,该线程将value属性更新为call()方法的结果
  4. 调度对FX应用程序线程的调用,该线程将state属性更新为SUCCEEDED

当然,最后一步将调用通过state属性注册的侦听器,并且由于状态更改是在上调用的,所以这些监听器的handle()方法也会调用。

要充分理解这是如何工作的,请参阅source code

通常,应用程序可能希望多次执行这些任务,并监视表示所有进程的当前状态(即“运行”现在意味着一个实例正在运行,等等)。Service类只是通过一个createTask()方法提供了一个包装器。当Service启动时,它通过调用createTask()获得一个Task实例,通过它的Executor执行它,并相应地转换它自己的状态。

当然,有许多并发用例不适合(至少是干净的) TaskService实现。如果您有一个后台Thread在整个应用程序期间运行(因此它代表一个连续的过程,而不是一个一次性的任务),那么Task类就不是很适合的了。这方面的例子可能包括一个游戏循环,或者(可能)轮询。在这些情况下,最好使用自己的ThreadPlatform.runLater()来更新UI,但当然,您必须处理两个线程可能访问的任何变量的适当同步。根据我的经验,值得花一些时间考虑这些需求是否可以重新组织成适合于TaskService模型的东西,就好像可以这样做一样--结果代码结构通常更干净,更易于管理。当然,在某些情况下,情况并非如此,在这种情况下,使用ThreadPlatform.runLater()是合适的。

关于轮询的最后一个评论(或定期安排的背景任务的任何其他要求)。Service类看起来是一个很好的候选类,但是很难有效地管理周期性。JavaFX 8引入了一个ScheduledService类,它很好地处理了这个功能,还增加了对后台任务重复失败等情况的处理。

票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25224323

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档