当前位置: 首页 > 知识库问答 >
问题:

JavaFX 2:背景和平台。runLater与任务/服务

冉绯辞
2023-03-14

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

我在后台工作中使用了一个基于后台线程的模型,该模型调用平台。稍后运行以更新UI。

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

现在在JavaFX2中,它们使用任务和服务实现了所有这些并发性,这表明使用它们更好。但我没有看到任何能实现我所说目标的例子。

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

那么,如何根据模型实际更新视图的内容呢?我应该打电话给平台吗。从任务中稍后运行?如果没有,机制是什么?如何捕捉任务成功的时间并获得更新视图的结果(实际模型的更新)?

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

共有1个答案

仇承志
2023-03-14

任务服务类旨在鼓励在GUI编程中的某些(但不是所有)常见场景中进行良好实践和正确使用并发

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

Task类为这种功能提供了一个抽象,并表示一个执行并产生结果的“一次性”任务。调用()方法将在后台线程上执行,用于返回进程的结果,FX应用程序线程上会通知任务完成时的事件侦听器。强烈建议开发人员使用不可变状态初始化任务实现,并让调用()方法返回不可变对象,从而保证后台线程和FX应用程序线程之间的正确同步。

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

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

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属性,该属性使用常规JavaFXObjectProperty实现。Task本身被包装在Callable的私有实现中,Callable实现是传递给超类构造函数的对象。因此,Callablecall()方法实际上是在后台线程中执行的方法。Callablecall()方法实现如下:

  1. 在FX应用程序线程上安排调用(即使用更新状态的Platform.runLater()),首先是计划的,然后是运行的
  2. 调用任务的调用方法(即用户开发的调用方法
  3. 在FX应用程序线程上安排一个调用,将值属性更新为调用方法的结果
  4. 在FX应用程序线程上安排调用,将状态更新为成功

最后一步当然会调用注册了state属性的监听器,由于状态更改是在FX应用程序线程上调用的,所以这些监听器的handle()方法也会调用。

有关其工作原理的完整理解,请参阅源代码。

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

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

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

 类似资料:
  • 问题内容: 我对JavaFX中的/ 概念非常困惑。 我在后台工作中使用了基于后台线程的模型,该模型要求对UI进行任何更新。 假设我对进度栏等不感兴趣。我正在对模型进行一些实际工作,这些工作必须在GUI的视图中进行更新(例如,根据后台的某些连接随时间更新的参与者列表,基于某些用户输入的参与者列表,按年龄分类)和起源)。这是我通常在启动并使用的后台线程中实现的。 现在,在JavaFX 2中,它们使用s

  • 我对如何从这个网站将大量模板JSON数据加载到我的应用程序中而不减慢主UI线程感到非常陌生和困惑。我可以将要从JSON响应加载的数据加载到我的recycler视图中,但单击底部显示该数据的导航选项卡需要很长时间才能加载到页面上。 我明白这是因为数据没有正确加载到后台线程,这就是我感到困惑的地方。 我希望能够从后台任务返回一个ArrayList,并将填充的ArrayList作为我的recyclerv

  • 服务管理平台是一个开发者使用的平台,它能够通过可视化配置,自动生成接口服务,同时为了安全性和差异化服务,加入了接口权限控制功能,同时一键发布到数据汇总平台,并查看相关帮助文档,接口参数等信息。最后全程监控服务调用过程,记录相关日志服务信息,确保系统稳定,排查系统异常,同时提供相关接口调用统计分析数据。 服务的功能架构如下: 四个模块之间通过接口服务串联起来。     1. 首先需要进行服务配置,配

  • 我想知道这一点,经过一点挖掘,找到了这个资源,链接在这个答案中。 该资源指出: 背景与背景色 比较18个色板在页面上呈现100次为小矩形,一次带有背景,一次带有背景颜色。 现在,我可以想象要快得多,因为资源也认为: 我认为当浏览器看到

  • 我对平台有几个问题。稍后运行。我有一个JavaFX应用程序类。在这个类中,我运行一个线程(线程从网络套接字读取数据)。 现在,当我在线程中创建一个新的时,系统会抛出一个执行(JavaFX事件调度器线程和我的netork-read线程不一样)-我理解这种行为。 但另一方面,我将来自网络阅读器的文本附加到现有的文本区域,或者在列表视图中添加/删除一些项目 我的问题: JavaFX事件调度器线程何时引发

  • 我是pycharm的新用户,我很难运行一个简单的文件来导入熊猫。我有以下问题: 每当我打开项目时,“扫描文件到索引”都需要很长时间。 我可以看到在索引中,它使用了Anaconda2中的包/文件,这就是为什么我的程序需要很长时间才能运行。 我已经尝试使某些文件夹“排除”,如此处所述 PyCharm 4.0.5 挂在“扫描文件以索引”后台任务上,我还删除了此处所述的“缓存”文件夹 Intelli J