当前位置: 首页 > 面试题库 >

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

鄢雅畅
2023-03-14
问题内容

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

我在后台工作中使用了基于后台线程的模型,该模型要求Platform.runLater对UI进行任何更新。

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

现在,在JavaFX 2中,它们使用Tasks和Services 具有所有这些并发性,这表明最好使用它们。但是我看不到任何实现我所讨论内容的示例。

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

那么,如何实际基于模型更新视图的内容?我应该Platform.runLater从内部打电话Task吗?如果没有,机制是什么?如何捕获任务成功完成并获取结果(实际模型的更新)以更新视图?

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


问题答案:

TaskService课程旨在鼓励良好做法,在GUI编程的一些(但不是全部)常见的场景正确使用并发。

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

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

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

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

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本身被包裹在一个私人的实施CallableCallable执行是传递给父类的构造对象。因此,Callablecall()方法实际上是在后台线程中执行的方法。所述Callablecall()方法是这样实现的:

  1. 安排FX Application线程上的调用(即使用Platform.runLater()),state以将SCHEDULED,更新为,然后更新为RUNNING
  2. 调用的call()方法Task(即用户开发的call()方法)
  3. 安排对FX Application Thread的调用,以将value属性更新为call()方法的结果
  4. 安排对FX Application Thread的调用,以将该state属性更新为SUCCEEDED

当然,这最后一步将调用在state属性中注册的侦听器,并且由于状态更改是在FX Application
Thread上调用的,因此这些侦听器的handle()方法也将被调用

要全面了解其工作原理,请参见源代码。

通常,应用程序可能需要多次执行这些任务,并监视代表所有进程的当前状态(即“正在运行”现在意味着一个实例正在运行,等等)。的Service类简单地通过为此提供了一个包装createTask()方法。当Service启动时,它Task通过调用获取实例,通过实例createTask()执行该实例Executor,并相应地转换其自身的状态。

当然,有很多并发用例不适合(至少是完全不适合)TaskService实现。如果您有一个Thread在整个应用程序运行期间都在运行的背景(因此它表示一个连续的过程,而不是一次性的任务),则Task该类不是一个很好的选择。例如,游戏循环或(可能)轮询。在这些情况下,最好使用自己的Threadwith
Platform.runLater()来更新UI,但当然,您必须处理两个线程都可以访问的任何变量的正确同步。以我的经验,值得花些时间考虑这些需求是否可以重新组织成适合于Task或的东西。Service模型,好像可以做到这一点一样,生成的代码结构通常更简洁,更易于管理。当然,在某些情况下并非如此,但在这种情况下使用
ThreadPlatform.runLater()是合适的。

关于轮询的最后一项评论(或对定期计划的后台任务的其他任何要求)。这个Service班级看起来像是一个很好的候选人,但是事实证明要有效地管理周期性非常困难。JavaFX
8引入了一个ScheduledService很好地处理此功能的类,并且还增加了对诸如后台任务反复失败等情况的处理。



 类似资料:
  • 我对JavaFX中/的概念感到非常困惑。 我在后台工作中使用了一个基于后台线程的模型,该模型调用平台。稍后运行以更新UI。 假设我对进度条之类的东西不感兴趣。我正在对我的模型做一些实际的工作,这些工作必须在GUI的视图中更新(例如,根据后台的某些连接随着时间的推移而更新的参与者列表,基于某些用户输入的参与者列表,按年龄和来源分类)。这是我通常通过启动后台线程来实现的,在其中我使用。 现在在Java

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

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

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

  • 在超文本标记语言中,我什么时候使用颜色,背景颜色和背景标签有什么区别? 有什么区别?

  • Ths是我的服务班 }