invokeAndWait
Werner Lehmann
lehmann at media-interactive.de
Fri Jan 4 08:51:21 PST 2013
Here's another one. Similar code, with exception propagation (wrapped
into a runtime exception). And yes, I have deadlocked myself already
with it :-)
> /**
> * Runs code on the FX thread and waits for completion. This is similar to
> * {@link SwingUtilities#invokeAndWait(Runnable)} which is not provided
> * by the JavaFX {@link Platform}. If the runnable throws any exception it
> * will be wrapped into a {@code RuntimeException} thrown by this method.
> *
> * @param runnable the runnable to execute on the FX thread
> * @see #runAndWait(Callable)
> */
> public static void runAndWait(final Runnable runnable)
> {
> if (Platform.isFxApplicationThread())
> try { runnable.run(); }
> catch (RuntimeException e) { throw new RuntimeException("Exception in runnable: " + e.getMessage(), e); } // need to wrap a runtime exception, too, so that the actual exception is always the cause of the thrown exception
> else
> {
> final CountDownLatch latch = new CountDownLatch(1);
> final MintObjectHolder<Exception> exception = new MintObjectHolder<Exception>();
> Platform.runLater(new Runnable() {
> @Override
> public void run()
> {
> try { runnable.run(); }
> catch (RuntimeException e) { exception.set(e); }
> latch.countDown();
> }
> });
>
> try { latch.await(); }
> catch (InterruptedException e) { throw new RuntimeException(e); }
>
> if (exception.get() != null)
> throw new RuntimeException("Exception in runnable: " + exception.get().getMessage(), exception.get());
> }
> }
Same approach to support a callable instead of a runnable:
> /**
> * Runs code on the FX thread and waits for completion, returning the callable
> * result. If the runnable throws any exception it will be wrapped into a
> * {@code RuntimeException} thrown by this method.
> *
> * @param callable the callable to execute on the FX thread
> * @see #runAndWait(Runnable)
> */
> public static <T> T runAndWait(final Callable<T> callable)
> {
> if (Platform.isFxApplicationThread())
> try { return callable.call(); }
> catch (Exception e) { throw new RuntimeException("Exception in callable: " + e.getMessage(), e); }
>
> final CountDownLatch latch = new CountDownLatch(1);
> final MintObjectHolder<T> result = new MintObjectHolder<T>();
> final MintObjectHolder<Exception> exception = new MintObjectHolder<Exception>();
> Platform.runLater(new Runnable() {
> @Override
> public void run()
> {
> try { result.set(callable.call()); }
> catch (Exception e) { exception.set(e); }
> latch.countDown();
> }
> });
>
> try { latch.await(); }
> catch (InterruptedException e) { throw new RuntimeException(e); }
>
> if (exception.get() != null)
> throw new RuntimeException("Exception in callable: " + exception.get().getMessage(), exception.get());
>
> return result.get();
> }
And another one to have the runnable called after some delay:
> /**
> * Runs the provided runnable after the specified delay. The runnable
> * will be executed on the FX thread. This method can be called on
> * any thread.
> *
> * @param delay the duration to wait until starting the runnable
> * @param runnable the runnable to start after a delay
> */
> public static void runLater(Duration delay, final Runnable runnable)
> {
> final Timeline timeline = TimelineBuilder.create()
> .keyFrames(new KeyFrame(delay))
> .onFinished(new EventHandler<ActionEvent>() {
> @Override public void handle(ActionEvent event) { runnable.run(); }
> })
> .build();
>
> if (Platform.isFxApplicationThread())
> timeline.play();
> else
> Platform.runLater(new Runnable() {
> @Override public void run() { timeline.play(); }
> });
> }
The code needs the following holder class:
> /**
> * Holds a mutable reference. In this way the holder instance itself can be
> * stored in a final variable and thus used in anonymous classes.
> */
> private static final class MintObjectHolder<T>
> {
> private T object;
> public T get() { return object; }
> public void set(T object) { this.object = object; }
> }
Rgds
Werner
More information about the openjfx-dev
mailing list