Reputation: 15769
I learned that in JavaFX the equivalent of
SwingUtilities.invokeLater(new Runnable() {
public void run() {
dosomething();
}
});
might simply be
Platform.runLater(() ->{ dosomething()};
for a long running task I learned that you need to wrap things with a Task like:
Task<Void> task = new Task<Void>() {
@Override
public Void call() {
dosomething();
}
};
new Thread(task).start();
Now it would be great to be able to have a similar lambda shortcut like
TaskLaunch.start(() -> dosomething());
I found
discussing some of the issues around this and tried:
package com.bitplan.task.util;
import java.util.concurrent.Callable;
import javafx.concurrent.Task;
/**
* this is a utility task to launch tasks with lambda expressions
*
* @author wf
*
*/
public class TaskLaunch {
/**
*
* @param callable
* @return the new task
*/
public static <T> Task<T> task(Callable<T> callable) {
Task<T> task = new Task<T>() {
@Override
public T call() throws Exception {
return callable.call();
}
};
return task;
}
}
with a JUnit test:
Integer counter=0;
boolean running=false;
public Integer increment() {
running=true;
while (running) {
counter++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
return counter;
}
/**
* @throws Exception
*/
@Test
public void testTaskLaunch() throws Exception {
// https://stackoverflow.com/questions/30089593/java-fx-lambda-for-task-interface
Task<Integer> task=TaskLaunch.task(() -> increment());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
//
}
running=false;
assertTrue(task.get()>10);
}
Which doesn't quite do what I'd like to see yet. The issue seems to be that the lambda expression runs in the same Thread and the
new Thread(task).start();
part needs to be integrated.
What is needed to get (at least close to) the short one liner mentioned above?
Is a
TaskLaunch.start(() -> dosomething());
feasible?
based on @Damianos proposal https://stackoverflow.com/a/44817217/1497139
I tried:
package com.bitplan.task;
import java.util.concurrent.Callable;
import javafx.concurrent.Task;
/**
* this is a utility task to launch tasks with lambda expressions
*
* @author wf
*
*/
public class TaskLaunch<T> {
Thread thread;
Task<T> task;
Callable<T> callable;
Throwable throwable;
Class<T> clazz;
public Thread getThread() {
return thread;
}
public void setThread(Thread thread) {
this.thread = thread;
}
public Task<T> getTask() {
return task;
}
public void setTask(Task<T> task) {
this.task = task;
}
public Callable<T> getCallable() {
return callable;
}
public void setCallable(Callable<T> callable) {
this.callable = callable;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
public Class<T> getClazz() {
return clazz;
}
public void setClazz(Class<T> clazz) {
this.clazz = clazz;
}
/**
* construct me from a callable
*
* @param callable
*/
public TaskLaunch(Callable<T> callable, Class<T> clazz) {
this.callable = callable;
this.task = task(callable);
this.clazz = clazz;
}
/**
*
* @param callable
* @return the new task
*/
public static <T> Task<T> task(Callable<T> callable) {
Task<T> task = new Task<T>() {
@Override
public T call() throws Exception {
return callable.call();
}
};
return task;
}
/**
* start
*/
public void start() {
thread = new Thread(task);
thread.start();
}
/**
* start the given callable
* @param callable
* @param clazz - the return Type class
* @return - the launch result
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static TaskLaunch start(Callable<?> callable, Class<?> clazz) {
TaskLaunch<?> launch = new TaskLaunch(callable, clazz);
launch.start();
return launch;
}
}
and changed the test to:
/**
* @throws Exception
*/
@SuppressWarnings("unchecked")
@Test
public void testTaskLaunch() throws Exception {
// https://stackoverflow.com/questions/30089593/java-fx-lambda-for-task-interface
TaskLaunch<Integer> launch = TaskLaunch.start(()->increment(),Integer.class);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
//
}
running=false;
assertTrue(launch.getTask().get()>10);
}
This is close to what i am up to but I get:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at javafx.application.Platform.runLater(Platform.java:83)
at javafx.concurrent.Task.runLater(Task.java:1225)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1417)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:745)
At least TaskLaunch now wraps:
Some of these 5 items might be redundant and available from the standard java concepts. I think at least its handy to have quick access to these after running things from a one liner.
Hope this gets to a working state and thanks for the help!
Upvotes: 0
Views: 4590
Reputation: 15769
The answer is now in the question based on Damianos hint.
The workaround for the exception I found is
com.sun.javafx.application.PlatformImpl.startup(() -> {
});
But seems a little bit hacky ...
Upvotes: -1
Reputation: 8363
Doing this will cause you to lose the ability to update stuff back to the UI thread natively supported by Task
class. On the other hand, I do agree this can be useful if you want to do something in background in "do-and-forget" style.
The problem is just like what you said - you didn't add new Thead()
and Thread.start()
in. Do this:
public static void runInBackground(Runnable runnable) {
Task<Void> task = new Task<>() {
@Override
public Void call() throws Exception {
runnable.run();
return null;
}
};
new Thead(task).start();
}
runInBackground(() -> System.out.println(Thread.currentThread().hashCode()));
Note that your Task
can no longer be non-void, because it cannot return anything back now. Your lambda needs to be able to reference the Task
object to return a result asynchronously - that is never going to be possible using lambda.
Upvotes: 1
Reputation: 16264
This is sort of a traditional XY problem.
A Task
is much more than just a background thread, hence for this you can use regular threads. It's the beauty of the properties!
The real benefit of using Task
is that all state changes and progress updates can safely be observed and bound to a live scene, while doing all the background work on a different thread. It's the work of the class to do the heavy-lifting and call Platform.runLater
.
The reason you need a subclass and not a runnable is so you can call its protected updateXxx()
methods without worrying for threading issues.
With this said, you'll have no benefit if this would've been a single line code. For this use simple threads.
Hope this helps.
Upvotes: 1