Where to add events to nodes? - java

Can you tell me, where I should declare my event listeners for Nodes, which are added out side of my controller class?
The best way is to explain it with this example:
I have my controller:
public class FXMLDocumentController implements Initializable {
#FXML
private AnchorPane root;
#Override
public void initialize(URL url, ResourceBundle rb) {
TestTask test = new TestTask(root);
Thread th = new Thread(test);
th.start();
}
}
And then I have the Task, which is started in the initialize method:
public class TestTask extends Task<Void>{
private AnchorPane root;
public TestTask(AnchorPane root){
this.root = root;
}
#Override
protected Void call() throws Exception {
Button btn = new Button("TestButton");
Platform.runLater(() -> { root.getChildren().add(btn); });
return null;
}
}
What I'm doing here? I have a FXML with an AnchorPane as root element. It has the id root. So now I start the Task in which I add one Button to my root node. Now I want to register an action event to the button. My question is now, where can/should I register the listener. Normally I register them in the controller, but here I can't do this because the Button only exists in the Task class. I could register it in the Task class but I think that it not scales good with large applications. The other way would to return the node back, so that I can access it in the controller class, but here I have to check if it is already added (to do this I have to call task.get(), which stops my application. So now could you tell me: what is the best way to register the listener for the node?

Don't create the UI in the background thread. There is (at best) very rarely a need to do this. If you need to perform some long-running task that retrieves data you need in order to create your UI, return the data from the task, and create the UI in the task's onSucceeded handler:
public class SomeControllerClass {
#FXML
private AnchorPane root ;
public void initialize() {
Task<SomeDataType> task = new MyTask();
task.setOnSucceeded(e -> {
// this method executed on FX Application thread.
SomeDataType result = task.getValue();
// now create UI and update root, using the data retrieved
});
Thread thread = new Thread(task);
thread.start();
}
}
and
public class MyTask extends Task<SomeDataType> {
#Override
public SomeDataType call() {
SomeDataType result = longRunningProcess();
return result ;
}
}

Related

How to implement a background thread using java.util.concurrent package?

This is the code I used first but in latest Android version AsyncTask class is deprecated and
therefore it was not responding and then I used the Thread class but that class is also not working.
I want the same result as I was getting with the AsyncTask class.
I know that I have to use some executor class of java.util.concurrent package but don't know which and how to use it.
Please help me with this thing.
private static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2016-01-01&endtime=2016-05-02&minfelt=50&minmagnitude=5";
EarthquakeAsyncTask task = new EarthquakeAsyncTask();
task.execute(USGS_REQUEST_URL);
private class EarthquakeAsyncTask extends AsyncTask<String, Void, Event> {
#Override
protected Event doInBackground(String... urls) {
// Perform the HTTP request for earthquake data and process the response.
Event result = Utils.fetchEarthquakeData(urls[0]);
return result;
}
#Override
protected void onPostExecute(Event result) {
// Update the information displayed to the user.
updateUi(result);
}
}
private static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2016-01-01&endtime=2016-05-02&minfelt=50&minmagnitude=5";
earthquakeRunnable runnable = new earthquakeRunnable(USGS_REQUEST_URL);
runnable.start();
private class earthquakeRunnable extends Thread{
String urls;
earthquakeRunnable(String url){
this.urls = url;
}
#Override
public void run() {
// Perform the HTTP request for earthquake data and process the response.
Event result = Utils.fetchEarthquakeData(urls);
// Update the information displayed to the user
updateUi(result);
}
}
Here's an example of how you might use an ExecutorService within your Activity/Fragment:
// Create some member variables for the ExecutorService
// and for the Handler that will update the UI from the main thread
ExecutorService mExecutor = Executors.newSingleThreadExecutor();
Handler mHandler = new Handler(Looper.getMainLooper());
// Create an interface to respond with the result after processing
public interface OnProcessedListener {
public void onProcessed(Event result);
}
private void processInBg(final String url, final boolean finished){
final OnProcessedListener listener = new OnProcessedListener(){
#Override
public void onProcessed(Event result){
// Use the handler so we're not trying to update the UI from the bg thread
mHandler.post(new Runnable(){
#Override
public void run(){
// Update the UI here
updateUi(result);
// ...
// If we're done with the ExecutorService, shut it down.
// (If you want to re-use the ExecutorService,
// make sure to shut it down whenever everything's completed
// and you don't need it any more.)
if(finished){
mExecutor.shutdown();
}
}
});
}
};
Runnable backgroundRunnable = new Runnable(){
#Override
public void run(){
// Perform your background operation(s) and set the result(s)
Event result = Utils.fetchEarthquakeData(url);
// ...
// Use the interface to pass along the result
listener.onProcessed(result);
}
};
mExecutor.execute(backgroundRunnable);
}
Then, wherever you need to trigger your background processing:
processInBg("some_url", true);
Depending on your situation, you'll want to customize your implementation of ExecutorService to better suit your needs.

JavaFX Multithreading

I am facing one problem in my JavaFx application.
Preface: I don't want to be specific to my application only but want to be generalized so that people like me will get an idea on similar situation.
Background: Implementing a Javafx application using fxml file and multi-threading concept.
Summary: I tried make an application which basically uses multi threading to do some task, and once multi- threading is completed, it will sequentially move to another task. While doing the multi-threading the Main GUI freezes.
What I did,
//This is Main class
Parent Root -> defined on top
public Parent createContent(){
try{
root = FXMLLoader.load(getClass().getResource("Layout.fxml"));
}catch { .....}
}
public void start(){
stage.setScene(new Scene(createContent()));
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
Thread.sleep(1000);
return null ;
}
};
task.setOnSucceeded(event->{
stage.show();
});
new Thread(task).run();
}
public static void main(String[] args) {
launch(args);
}
// This is inside the controller on button click event
#FXML
private void start(ActionEvent event) { <---- This one is button click event
Thread t1 = new Thread(new Mul());
t1.start();
Thread t2 = new Thread (new Mul());
t2.start();
}
// Finally
public class Mul implements Runnable {
public void type() {
for (int a = 0; a < 200000; a++) {
System.out.println( Thread.currentThread().getName()+ " says " + a);
}
}
#Override
public void run() {
type();
}
}
Now, here is the outcome.
If I just start the threads from the controller then My main application does not freezes while the thread are running in background. However, since the application runs in a sequence, i.e the next step only works when the threads complete their work
I can use t1.join() and t2.join() , but doing so will freezes my main application(Main application here is the main GUI) and I cannot proceed with it until the threads are completed.
What could be the optimal solution, so that I can use multi threading without blocking the main application or What am I doing wrong here? (info, I came to this solution by following up different suggestions from Stack overflow and google)
Why not do
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
Mul m1 = new Mul();
m1.run();
Mul m2 = new Mul();
m2.run();
return null ;
}
};
new Thread(task).start();
If you really want to "chain" different tasks, call one from the other's onSucceeded handler:
Task<Void> task1 = new Task<Void>() {
#Override
public Void call() {
new Mul().run();
return null ;
}
};
Task <Void> task2 = new Task<Void>() {
#Override
public Void call() {
new Mul().run();
return null ;
}
};
task1.setOnSucceeded(e -> new Thread(task2).start());
new Thread(task1).start();
Obviously this is cleaner if you make Mul a Task subclass instead of a Runnable, and it's better to use an Executor with daemon threads, etc, but this should give you the idea.

How to use javaFX Task in my case - Not duplicate

I know there are answers for my questions but I did not understand the problem in my code.
Why I get :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
I'm trying to add text from a Task to a Text flow using a method in the controller class, for some reason the program fails on .getChildren() method .
Call for Spliter in the controller class:
btnSplit.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
Thread split = new Thread(new Spliter(custToSplit, Controller.this));
split.setDaemon(true);
split.start();
}
});
class Spliter constractor :
public Spliter(File f, Controller c){
this.c = c;
this.cust = f;
}
c.updateHebFlow("Dir created: "+ newDir.getAbsolutePath() , INFO_TEXT);
Part of the Controller class :
#FXML
private TextFlow hebFlow;
#Override
public void initialize(URL location, ResourceBundle resources) {
assert hebFlow != null : "fx:id=\"hebFlow\" was not injected: check your FXML file 'MainXml.fxml'.";
public void updateHebFlow(String text,String type){
normalText = new Text();
errorText = new Text();
errorText.setFill(Color.RED);
infoText = new Text();
infoText.setFill(Color.BLUE);
switch(type){
case(ERROR_TEXT) :
errorText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), errorText);
break;
case(INFO_TEXT) :
infoText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), infoText);
break;
case(NORMAL_TEXT) :
normalText.setText(text);
hebFlow.getChildren().addAll(new Text("/n"), normalText);
break;
}
}
}
Call for updateHebFlow in Spliter class:
try{
c.updateHebFlow("Script by TulTul", INFO_TEXT);
}catch (Exception e){
e.printStackTrace();
}
From what I understand I cant change the UI from other class other then the controller so Ive made a method in the controller class that will make the changes and call it in the Task class, why I'm getting this exception?
If this is wrong what is the wright way ?
From what I understand I cant change the UI from other class other then the controller
Actually the correct statement is: "You cannot change the UI from any thread other than the JavaFX UI thread". So, the solution is to use Platform.runLater() from the Splitter as:
// Java 8
Platform.runLater(() -> {
c.updateHebFlow("Script by TulTul", INFO_TEXT);
});
// Java 7
Platform.runLater(new Runnable() {
public void run() {
c.updateHebFlow("Script by TulTul", INFO_TEXT);
}
});
Platform.runLater() is guaranteed to run the blocks in the JavaFX UI thread and in call order.

Running a stream in background with Swing MVC

I have a View, Model, Controller classes. The View and Model are passed to the Controller constructor and all three are initialized with EventQueue.invokeLater() one after another.
The view has a JList component. My application requires to run a stream of text lines and to put each line in the JList component as they arrive. The stream architecture is done with Listeners, so as soon as an element arrives from the stream a StreamListener is notified. I need to start or stop the stream.
Now my problem is that I'm not sure how to model such a scenario. Of course setting the Controller as the StreamListener and then using a SwingWorker inside the Controller will solve the problem. However I thought it is the jobs of the Model to handle the streaming and business logic because the role of the Controller is just to facilitate the communications between the View and the Model.
One way I'm thinking to solve this is by defining a SwingPropertyChangeSupport in the Model and let the Controller implement PropertyChangeListener. Then in the Model I create a SwingWorker and call publish() every time an element from the stream arrives. Then from process() I call SwingPropertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue) which then will cause the Controller to invoke its implemented method propertyChange(PropertyChangeEvent evt) which will finally cause the JList to add the element.
However I'm not sure if my approach is correct nor if the code will run on the Event Dispatching Thread. I hope if you can help.
public class Model {
SwingPropertyChangeSupport changeFirer;
Task task;
class Task extends SwingWorker<Void, Element> implements StreamListener {
Stream stream;
protected Void doInBackground() {
stream = new Stream();
stream.start();
}
public void onRecieveing(Element element) {
public(element);
}
protected void process(List<Element> elements) {
Element element = elements.get(elements.size()-1);
changeFirer.firePropertyChange("updateList", null, element);
}
}
}
The Controller:
public class Controller implements PropertyChangeListener {
Model model;
View view;
public Controller(View view, Model model) {
this.view = view;
this.model = model;
model.setChangeFirer(this);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
Element element = evt.getNewValue();
view.updateList(element);
}
}
Main method:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
View view = new View();
Model model = new Model();
Controller controller =
new Controller(view, model);
view.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
My solution is correct, propertyChange() runs on EDT and fires correctly.

Android - Lost my way in this multithreaded approach - pass data and thread execution to higher hierarchy object

I really cannot think anymore, somehow I am almost at the end but now I hope, this will not be a dead-end-street.
I want to pass data from a inherited thread object back to my parent object.
And also return to main thread all in one.
Here is my Parent-Object
public class ControllerBase implements IController , IHandleRequestOnChange, IHandlesCustomTimer{
private QueueWorkerThread _QueueWorker;
// some other vars and methods
// ctor and so on .....
// initializer
public void init(){
// some other code
_QueueWorker = new QueueWorker();
_QueueWorker.SetEventListener(this); // i want to announce this object from _QueueWorker
_QueueWorker.start() // starts the thread
// other initializations
}
#Override
public void OnQueueWorkerReady(DataToPass){
// from IHandleRequestOnChange
// ALL INSIDE THIS CODE SHALL BE PROCESSED IN UI THREAD. BUT HOW ?
DataReceived dataRec = new DataReceived();
dataRec = this.Request(DataToPAss);
this.ApplyDataToUIControls(dataRec);
}
}
Here is my QueueWorkerThread :
public class QueueWorkerThread extends Thread implements IRequestQueueProcessed{
// ctor
public QueueWorkingThread(){
super();
}
// other variables and methods
IHandlesRequestOnChange _Listener;
public void Enque(DataToPass Data){
this.add(Data);
}
public void SetEventListener( IHandlesRequestOnChange pEventListener) {
this._Listener = pEventListener;
#Override
public void run(){
// here a LinkedBlockingQueue is polled
// AND UNDER SOME CONDITIONS I WANT TO PASS THE POLLED DATA BACK TO
// THE PARENT OBJECT AND RETURN TO MAIN THREAD. HOW CAN I ACHIEVE THIS?
if(AcertainConditionIsMet == true){
// I want to return to UI thread
this.OnQueueWorkerReady(the_polled_data)
}
// and this thread shall proceed and do some other stuff......
}
#Override
public void OnQueueWorkerReady(TableToPass Data){
// of interface IRequestQueueProcessed
//
// calling parents callback via interface-casting
((IHandleRequestOnChange)_Listener).OnQueueWorkerReady(null, Data);
// this passes execution AND DATA to my parent object BUT I do not return to main thread
}
}
I think you must share your handler in the application object to be able to pass data back in handler's message. You could have for instance:
public class MyApplication extends Application {
private Handler handler;
public Handler getHandler(){ return handler;}
public void setHandler(Handler handler){ this.handler = handler;}
private static MyApplication instance;
public static MyApplication getMyApplication(){return instance;}
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
}
Notice the static method to be able to retrieve the application object without a context (like in your thread).
I suppose if you want to update your UI you should be inside some activity context, so in your activity, you declare the handler:
private Handler handler;
inside onCreate(), you can instantiate and store the handler in the app object:
handler = new Handler(){
public void handleMessage(android.os.Message msg) {
//In here you can do your UI update using the references to the views
}
};
((MyApplication)getApplication()).setHandler(handler);
EDIT:
You can now get an instance of your app object from your thread using the public static method..
Hope it helps.

Categories