我发现很多建议使用Timeline
和KeyFrame.onFinished
来安排 JavaFX 中的任务。但是AnimationTimer
doc说,它的handle
方法每秒被调用60次。
从文档中不清楚,但似乎Timeline
在内部使用AnimationTimer
。这是否意味着调度的时间线解决方案会产生CPU密集型轮询模式?如果这实际上是JavaFX的工作原理,建议采用其他什么方法进行调度?
答案 0 :(得分:4)
为JavaFX中的调度使用规则提供建议
鉴于Timeline的工作原理(见下文),可以推导出一些基本规则:
如果要安排修改场景图属性的快速操作,请使用时间轴。
如果您想要执行以下任何操作,请不要使用时间轴:
此外,对于在固定延迟后更新场景图的一次性执行,我喜欢使用PauseTransition。
特定问题和疑虑的答案
目前还不清楚文档,但似乎Timeline在内部使用了AnimationTimer。
不,Timeline内部不使用AnimationTimer。但是,时间轴仍然是基于脉冲的,并且将接收代码来更新时间线的当前状态,将在每个脉冲上调用(通常为每秒60次)。要了解脉冲的内容,请参阅下面的背景部分。
这是否意味着调度的时间轴解决方案会产生CPU密集型轮询模式?
是的,有一些开销,但它可能很小,可以忽略不计。无论您是否自己创建任何时间轴,JavaFX系统都将在每个脉冲上运行一些工作。除非你对每个脉冲进行大量动画制作或对每个脉冲进行大量工作,否则处理每个脉冲的时间将完全无关紧要,因为每个脉冲的工作很少。
如果这实际上是JavaFX的工作原理,建议采用其他什么方法进行调度?
时间轴有很多替代方案:
如果您使用的不是时间表,则需要注意:
背景资讯
时间线操作
例如,假设您有一个时间轴,其Keyframe的持续时间为1秒。每个脉冲仍会更新内部时间轴。这是必需的,因为时间轴不仅仅是一个调度程序,它还有其他属性和任务。例如,时间轴具有currentTime属性。 currentTime将更新每个脉冲,以便它始终准确地反映当前时间。同样,具有时间轴的内部逻辑将检查是否存在与时间轴关联的任何KeyValues,并根据其关联的Interpolator更新每个脉冲的值。内部逻辑将检查每个脉冲以查看时间轴是否完成,以调用onFinished处理程序。它还将评估当前时间与每个关键帧的持续时间,如果匹配,则触发关键帧的相应操作事件。
因此,时间轴就像一个调度程序,因为它可以在特定时间执行关键帧,但它不仅仅是一个调度程序,因为它还维护它的当前时间,当前状态和连续改变相关密钥的能力值。此外,您可以改变时间线的速率,方向和周期数,并将其暂停,然后恢复,这也与传统的调度程序不同。
时间轴的一个方面是因为它基于JavaFX系统中脉冲的回调;一切都在JavaFX应用程序线程上运行。因此,当您使用时间轴(甚至1000个)时,不会生成其他线程,因此从该角度来看,它是轻量级的。此外,时间轴调用的所有逻辑都发生在JavaFX应用程序线程上。因为所有内容都在JavaFX应用程序线程上运行,所以这使得时间轴非常适合操作活动JavaFX场景图的元素和属性(这种操作必须在JavaFX应用程序线程上执行)。但是,如果您想在相关的时间轴关键帧中执行大量CPU耗时的工作或阻止I / O,则时间轴将是一个糟糕的选择。这是因为JavaFX应用程序线程将在该工作发生时被阻止,从而冻结您的UI。
脉冲
JavaFX运行时系统基于pulses。
脉冲是一个事件,它向JavaFX场景图指示是时候将场景图上的元素状态与Prism同步。脉冲以每秒60帧(fps)的速度进行限制,并在场景图上运行动画时触发。即使动画未运行,当场景图中的某些内容发生变化时,也会调度脉冲。例如,如果更改按钮的位置,则会调度脉冲。
当触发脉冲时,场景图上元素的状态将向下同步到渲染层。脉冲使应用程序开发人员能够以异步方式处理事件。这一重要功能允许系统批量处理脉冲上的事件。
JavaFX动画系统存储已创建的所有animations和animation timers的列表,而不是垃圾回收。在每个脉冲上,它将遍历此列表。每个动画计时器都会在每个脉冲上调用它handle callback。每个时间轴或转换将类似地具有其内部状态更新并且适当地更新任何关联的键值或触发关键帧动作事件。
答案 1 :(得分:0)
final Runnable runnable = new Runnable() {
public void run() {
//Code goes here
}
}
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);