Javafx Apps get hang when perform Thread wait and notify - java

i am playing some animation from my apps by using infinite loop, working well. i need to make wait my thread when user want and again start when user want. For that i used wait and notify thread by clicking my root layout, first click make my thread wait and second click make my thread run. That also work as i want.
My problem is when i make click fast, it means when i make wait and also make notify instantly my Apps get hang.
So how I can fixed that problem???
below is my Code:
public class AboutC implements Initializable {
public VBox mainLayout;
#FXML
private
Label nameLvl = new Label();
#FXML
private
Label rollLvl = new Label();
#FXML
private
Label batchLvl = new Label();
#FXML
private
Label depLvl = new Label();
#FXML
private
Label uniLvl = new Label();
#FXML
private Circle circle = new Circle();
private int count = 0;
private boolean run = true;
private Thread thread;
private Task task;
private FadeTransition fd;
private RotateTransition rt;
private Timeline tm;
#Override
public void initialize(URL location, ResourceBundle resources) {
ArrayList<AboutDevelopers> list = new ArrayList<>();
list.add(....)
fd = new FadeTransition(Duration.seconds(4), mainLayout);
fd.setFromValue(0.2);
fd.setToValue(1.0);
fd.setCycleCount(2);
rt = new RotateTransition(Duration.seconds(4), circle);
rt.setByAngle(360);
rt.setAutoReverse(true);
rt.setCycleCount(2);
KeyFrame keyFrame = new KeyFrame(Duration.seconds(4), new KeyValue(circle.radiusProperty(), 0));
tm = new Timeline(keyFrame);
tm.setCycleCount(2);
tm.setAutoReverse(true);
task = new Task<Void>() {
#Override
synchronized public Void call() throws Exception {
int i = 0;
while (true) {
if (run) {
Platform.runLater(() -> {
nameLvl.setText(list.get(count).getName());
rollLvl.setText("Roll: " + list.get(count).getRoll());
batchLvl.setText("Batch: " + list.get(count).getBatch());
depLvl.setText("Department: " + list.get(count).getDepartment());
uniLvl.setText(list.get(count).getUniversity());
circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));
fd.play();
rt.play();
tm.play();
count++;
if (count >= list.size())
count = 0;
});
sleep(10000);
} else
wait();
}
}
};
thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
void setStage(Stage stage) {
stage.setOnCloseRequest(event -> {
thread.interrupt();
});
}
public void playThread(){
if (run) {
run = false;
} else {
if(!run){
synchronized (task) {
task.notify();
}
}
run = true;
}
}
}

run is not volatile and is written to outside of synchronized blocks. This means the task may never see the updated value.
Using Thread.sleep(10000) you do not release the lock on the Task which means the following could happen:
The task starts sleeping
The playThread method changes run to false
The playThread method is invoked again and tries to aquire a lock on the task object which the task still keeps itself leading to the calling thread to be blocked for up to 10 sec
To fix these issues, modify the run field only from a synchronized block and use wait with a timeout instead of sleep:
while (true) {
if (run) {
Platform.runLater(() -> {
nameLvl.setText(list.get(count).getName());
rollLvl.setText("Roll: " + list.get(count).getRoll());
batchLvl.setText("Batch: " + list.get(count).getBatch());
depLvl.setText("Department: " + list.get(count).getDepartment());
uniLvl.setText(list.get(count).getUniversity());
circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));
fd.play();
rt.play();
tm.play();
count++;
if (count >= list.size())
count = 0;
});
wait(10000);
} else
wait();
}
public void playThread(){
synchronized (task) {
run = !run;
if (run) {
task.notify();
}
}
}
This means however starting and stoping the task may speed up the update frequency...
Alternative:
Use a ScheduledExecutorService to schedule updates regularly:
// TODO: shut this down after you're done with it???
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
#Override
public void initialize(URL location, ResourceBundle resources) {
...
startTask();
}
private final Runnable updateRunnable = () -> {
Platform.runLater(() -> {
nameLvl.setText(list.get(count).getName());
rollLvl.setText("Roll: " + list.get(count).getRoll());
batchLvl.setText("Batch: " + list.get(count).getBatch());
depLvl.setText("Department: " + list.get(count).getDepartment());
uniLvl.setText(list.get(count).getUniversity());
circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));
fd.play();
rt.play();
tm.play();
count++;
if (count >= list.size())
count = 0;
}
});
};
private ScheduledFuture scheduledFuture;
private void startTask() {
scheduledFuture = executor.scheduleWithFixedDelay(updateRunnable, 0, 10000, TimeUnit.MILLISECONDS);
}
public void playThread() {
if (scheduledFuture == null) {
// nothing running currently
startTask();
} else {
scheduledFuture.cancel();
scheduledFuture = null;
}
}
Or in a way more suitable to JavaFX
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(10), evt -> {
nameLvl.setText(list.get(count).getName());
rollLvl.setText("Roll: " + list.get(count).getRoll());
batchLvl.setText("Batch: " + list.get(count).getBatch());
depLvl.setText("Department: " + list.get(count).getDepartment());
uniLvl.setText(list.get(count).getUniversity());
circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));
fd.play();
rt.play();
tm.play();
count++;
if (count >= list.size())
count = 0;
}
});
}));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
if (timeline.getStatus == Animation.Status.RUNNING) {
timeline.stop();
} else {
timeline.play();
}

Related

How to run Tasks in a non-GUI thread using Threadpools

I'm calculating three different values at the same time which is depending on the level state of my application, so I'm using Tasks for a faster calculation. However if I want to change the level during calculation it's not possible, because all the buttons are disabled during the calculation.
I've found out that I cannot cancel the tasks because my GUI freezes until the tasks are finished. Therefore I cannot cancel tasks, because they are already finished
My GUI has this buttonclick event
buttonIncreaseLevel.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.runLater(new Runnable() {
#Override
public void run() {
increaseLevelButtonActionPerformed(event);
}
});
}
});
It increases a level and starts this method
createTask(taskLeftEdge,EdgeType.LEFT);
createTask(taskBottomEdge,EdgeType.BOTTOM);
createTask(taskRightEdge,EdgeType.RIGHT);
pool.submit(taskRightEdge);
pool.submit(taskBottomEdge);
pool.submit(taskLeftEdge);
new Thread(new Runnable() {
#Override
public void run() {
try {
System.out.println("[Future] Started to wait for task (future)");
// A task is also its own future
ArrayList taskR = (ArrayList) taskRightEdge.get();
ArrayList taskB = (ArrayList) taskBottomEdge.get();
ArrayList taskL = (ArrayList) taskLeftEdge.get();
System.out.println("[Future] " + taskR);
System.out.println("[Future] " + taskB);
System.out.println("[Future] " + taskL);
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(FUN3KochFractalFX.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
).start();
The method for creating a task
private void createTask(Task task, EdgeType type){
if (task != null) {
application.cancelProgress(type);
task.cancel();
}
// There's a new task that performs some work
switch(type){
case LEFT:
taskLeftEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskLeftEdge;
edges.addAll(koch.generateLeftEdge());
break;
case BOTTOM:
taskBottomEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskBottomEdge;
edges.addAll(koch.generateBottomEdge());
break;
case RIGHT:
taskRightEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskRightEdge;
edges.addAll(koch.generateRightEdge());
break;
}
taskNumber++;
application.setProgress( task, type);
}
And the Task itself. The class Kochfractal is where the calculations are made
private static final Logger LOG = Logger.getLogger(EdgeGeneratorTask.class.getName());
private final String id;
private EdgeType type;
private KochFractal calc;
private FUN3KochFractalFX application;
public EdgeGeneratorTask(String id, EdgeType type, KochManager manager, FUN3KochFractalFX main) {
this.id = id;
this.type = type;
calc = new KochFractal(manager);
application = main;
}
#Override
protected ArrayList<Edge> call() throws Exception {
ArrayList<Edge> edges = new ArrayList<>();
switch(type){
case LEFT:
edges = calc.generateLeftEdge();
break;
case BOTTOM:
edges = calc.generateBottomEdge();
break;
case RIGHT:
edges = calc.generateRightEdge();
break;
}
int MAX = edges.size();
for (int i = 1; i <= MAX; i++) {
if (isCancelled()) {
break;
}
updateProgress(i, MAX);
updateMessage(id + " " + i);
}
return edges;
}
I expect these threads to be cancelled, with an action on my JavaFX GUI.
The threads should run on a background Thread instead of the GUI thread itself

JavaFX JSON parsing from URL threading freezes UI

I have this task run in a thread. The problem is that it freezes the UI every time it is executed. The freeze is longer when the internet is slow. How can I prevent the UI from freezing even if it is still gathering data from the url?
Task<Void> task = new Task<Void>(){
#Override
public Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run(){
String json = null;
try {
psname = null;
PumpSites n = table.getSelectionModel().getSelectedItem();
psname = n.getPs();
if(psname == "Cubacub")
json = readUrl(""); //read json from thingspeak.com webpage
else if(psname == "Canduman")
json = readUrl("");
} catch (InterruptedIOException iioe)
{
btn1.setTextFill(Color.RED);
btn2.setTextFill(Color.RED);
btn3.setTextFill(Color.RED);
btn4.setTextFill(Color.RED);
btn5.setTextFill(Color.RED);
btn1.setText("NULL");
btn2.setText("NULL");
btn3.setText("NULL");
btn4.setText("NULL");
btn5.setText("NULL");
}
catch (IOException ioe)
{
btn1.setTextFill(Color.RED);
btn2.setTextFill(Color.RED);
btn3.setTextFill(Color.RED);
btn4.setTextFill(Color.RED);
btn5.setTextFill(Color.RED);
btn1.setText("NULL");
btn2.setText("NULL");
btn3.setText("NULL");
btn4.setText("NULL");
btn5.setText("NULL");
}
catch (Exception e1) {
btn1.setTextFill(Color.RED);
btn2.setTextFill(Color.RED);
btn3.setTextFill(Color.RED);
btn4.setTextFill(Color.RED);
btn5.setTextFill(Color.RED);
btn1.setText("NULL");
btn2.setText("NULL");
btn3.setText("NULL");
btn4.setText("NULL");
btn5.setText("NULL");
}
Gson gson = new Gson();
Page page = gson.fromJson(json, Page.class);
for (Item item : page.feeds)
{
det2 = 1;
btn1.setText(item.field1);
btn2.setText(item.field2);
btn3.setText(item.field3);
btn4.setText(item.field4);
btn5.setText(item.field5);
f2 = Float.parseFloat(item.field2);
f3 = Float.parseFloat(item.field3);
//float f5 = Float.parseFloat(item.field5);
if (f2 <= 10.0)
{
btn1.setTextFill(Color.RED);
btn2.setTextFill(Color.RED);
}
else
{
btn1.setTextFill(Color.BLUE);
btn2.setTextFill(Color.BLUE);
}
if (f3 < 0.9 || f3 > 1.2)
{
btn3.setTextFill(Color.RED);
}
else
{
btn3.setTextFill(Color.BLUE);
}
/*if (f5 > 5.0)
{
btn5.setTextFill(Color.RED);
}
else
{
btn5.setTextFill(Color.BLUE);
}*/
btn4.setTextFill(Color.BLUE);
}
if(det2 == 0)
{
btn1.setTextFill(Color.RED);
btn2.setTextFill(Color.RED);
btn3.setTextFill(Color.RED);
btn4.setTextFill(Color.RED);
btn5.setTextFill(Color.RED);
btn1.setText("NULL");
btn2.setText("NULL");
btn3.setText("NULL");
btn4.setText("NULL");
btn5.setText("NULL");
}
det2 = 0;
}
});
Thread.sleep(10000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
The problem is that it freezes the UI every time it is executed. The freeze is longer when the internet is slow. How can I prevent the UI from freezing even if it is still gathering data from the url?
The UI thread freezes because you are still doing the all the logic on the JavaFX application Thread(Platform.runLater ).
You should do something like this instead:
public Void call() throws Exception
{
while (true)
{
try
{
//get json
} catch(Exception e)
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
//set buttons color and text here
}
}
}
//Rest of your logic here
}
}
The idea is that everything that is going to modify a UI component from a separate Thread should be handled in the Platform.runLater
If you use a background thread invoke Platform.runLater with a long-running Runnable as parameter, you've effectively achieved nothing. The Runnable is still run on the JavaFX application thread freezing your app.
Instead you should collect all the data on the background thread and process it to the point where you simply need to adjust some properties of the scene. Then you use Platform.runLater to do those updates.
But the good news is that there is a class designed for this scenario that could simplify your code a bit: ScheduledService.
Just make sure that you don't access the GUI in any way from the background thread (neither for reading nor for setting properties).
The following example simplified example should demonstrate the general approach. It calculates some multiples of the value chosen via Spinner on a background thread delaying 10 sec between each calculation:
#Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner(1, 100, 1);
// ensure the value is available in a way that allows synchronisation
final AtomicReference<Integer> input = new AtomicReference<>(spinner.getValue());
spinner.valueProperty().addListener((o, oldValue, newValue) -> input.set(newValue));
final int outputCount = 10;
GridPane root = new GridPane();
root.add(spinner, 0, 0, 2, 1);
// create output grid
Text[] output = new Text[outputCount];
for (int i = 1; i <= output.length; i++) {
Text text = new Text(Integer.toString(spinner.getValue() * i));
output[i - 1] = text;
root.addRow(i, new Text("Value multiplied by " + i + " = "), text);
}
root.setPrefWidth(300);
ScheduledService<int[]> service = new ScheduledService<int[]>() {
#Override
protected Task<int[]> createTask() {
return new Task<int[]>() {
#Override
protected int[] call() throws Exception {
// retrieve value and set it to null to denote a change
// that was already handled to avoid doing unnecessary
// work
Integer value = input.getAndSet(null);
int[] result = null;
if (value != null) {
int valueAsInt = value;
result = new int[outputCount];
for (int i = 0; i < outputCount; i++) {
result[i] = (i + 1) * valueAsInt;
}
}
// simpulate delay
Thread.sleep(2000);
return result;
}
};
}
};
service.valueProperty().addListener((o, oldValue, newValue) -> {
// update GUI
if (newValue != null) {
for (int i = 0; i < outputCount; i++) {
output[i].setText(Integer.toString(newValue[i]));
}
}
});
service.setPeriod(Duration.seconds(10));
// make sure service uses a daemon thread
service.setExecutor(Executors.newSingleThreadScheduledExecutor((Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}));
service.start();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
I recommend looking through the javadoc of ScheduledService to get familiar with it's capabilities. It also allows for things like reacting to exceptions in the task and specifying a backoff strategy.

How to run some code repeatedly after an interval that can changes?

I'm using a thread in JavaFX to repeat my code after an interval (initially 1s), but I want to be able change the interval that the thread is using to 500ms or 333ms based on user choice (I have a button in a menu bar to change for each choice). I did tried things like shutDown() if the user clicks on one of the buttons and initiate it again with the new value, but didn't work. Any ideas?
Here's the relevant part of my code:
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(() -> {
//refresh users, line and "guiche"
updateFila(usuarios, guiches, fila);
updateGuiche(guiches, fila, graphicsContext);
turno++;
//ends the code after the end of the line
if (done) {
exec.shutdown();
}
}, 0, 1000, TimeUnit.MILLISECONDS); //This is interval that I need to change after user choice
I know that I'm executing scheduleAtFixedRate() right now, but it was just to see if the logic was fine.
Additionally, I need to pause, resume and reset the thread, all based on user click.
You could use a Timeline to execute a event handler every second and set the rate at which the animation runs to the number of times the update should happen per second, i.e. 2 or 3...
In the following example I use 5 instead of 3 for a more recognizable effect:
#Override
public void start(Stage primaryStage) {
Line line = new Line(25, 125, 125, 125);
Rotate rotate = new Rotate(0, 125, 125);
line.getTransforms().add(rotate);
ToggleButton btn = new ToggleButton();
btn.textProperty().bind(Bindings.when(btn.selectedProperty()).then("5 Hz").otherwise("2 Hz"));
StackPane.setAlignment(btn, Pos.BOTTOM_LEFT);
// rotate by one 60th of a full rotation each time
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), evt -> rotate.setAngle((rotate.getAngle() + (360d / 60d)) % 360)));
timeline.setCycleCount(Animation.INDEFINITE);
// rate depends on button state
timeline.rateProperty().bind(Bindings.when(btn.selectedProperty()).then(5d).otherwise(2d));
Pane linePane = new Pane(line);
linePane.setMinSize(250, 250);
linePane.setMaxSize(250, 250);
StackPane root = new StackPane();
root.getChildren().addAll(linePane, btn);
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
timeline.play();
primaryStage.show();
}
The binding is simply an example of setting the update frequency. You could of course use different means to assign this value, e.g.
ComboBox<Duration> combo = new ComboBox<>();
Duration initial = Duration.seconds(1);
combo.getItems().addAll(initial, Duration.seconds(1/3d), Duration.seconds(1/2d));
combo.setValue(initial);
combo.valueProperty().addListener((observable, oldValue, newValue) -> timeline.setRate(1/newValue.toSeconds()));
If you use only single thread you can create your own implementation based on classic thread.
public class Worker extends Thread {
private static final Logger logger = Logger.getLogger(Worker.class);
private volatile int delayInSec = 1;
private CountDownLatch latch;
private final int STARTED = 0;
private final int STOPPED = 1;
private volatile int state = STOPPED;
public Worker(){}
#Override
public void run() {
logger.debug("enter to execution method");
while (!isInterrupted()) {
// stop if needed (it's additional feature)
if (state == STOPPED) {
logger.debug("stopped and locked");
try {
latch = new CountDownLatch(1);
latch.await();
} catch (InterruptedException e) {
logger.warning("got interruption while waiting for action ", e);
break;
}
logger.debug("awake");
}
// do your stuff here
try {
// then delay
logger.debug("go to sleep for %s sec.",delayInSec);
latch = new CountDownLatch(1);
latch.await(delayInSec, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.warning("got interruption while waiting for action ", e);
break;
}
}
logger.debug("exit from execution method");
}
public void startJob(){
state = STARTED;
logger.debug("started");
if (latch!=null)
latch.countDown();
}
public void stopJob(){
state = STOPPED;
logger.debug("stopped");
}
public void shutdown(){
logger.debug("shutdown");
interrupt();
}
public void changeDelay(int delayInSec) {
logger.debug("set new delay %s", delayInSec);
this.delayInSec = delayInSec;
}
}

Android postDelayed Handler Inside a For Loop?

Is there any way of running a handler inside a loop?
I have this code but is not working as it does not wait for the loop but executes the code right way:
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
public void run() {
// need to do tasks on the UI thread
Log.d(TAG, "runn test");
//
for (int i = 1; i < 6; i++) {
handler.postDelayed(this, 5000);
}
}
};
// trigger first time
handler.postDelayed(runnable, 0);
Of course when I move the post delayed outside the loop works but it does not iterate nor execute the times I need:
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
public void run() {
// need to do tasks on the UI thread
Log.d(TAG, "runn test");
//
for (int i = 1; i < 6; i++) {
}
// works great! but it does not do what we need
handler.postDelayed(this, 5000);
}
};
// trigger first time
handler.postDelayed(runnable, 0);
SOLUTION FOUND:
I need to use asyntask along with Thread.sleep(5000) in the doInBackground method:
class ExecuteAsyncTask extends AsyncTask<Object, Void, String> {
//
protected String doInBackground(Object... task_idx) {
//
String param = (String) task_idx[0];
//
Log.d(TAG, "xxx - iter value started task idx: " + param);
// stop
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//
Log.d(TAG, "xxx - iter value done " + param);
return " done for task idx: " + param;
}
//
protected void onPostExecute(String result) {
Log.d(TAG, "xxx - task executed update ui controls: " + result);
}
}
for(int i = 0; i < 6; i ++){
//
new ExecuteAsyncTask().execute( String.valueOf(i) );
}
Instead of using a for loop, you can let the Runnable instance call itself for a specific number of times. These calls will be posted to UI thread queue so, keep that in mind. Also, since the delay is quite large, make sure the event is still needed when you trigger it next time.
The following code should do it:
final Handler handler = new Handler();
int count = 0;
final Runnable runnable = new Runnable() {
public void run() {
// need to do tasks on the UI thread
Log.d(TAG, "Run test count: " + count);
if (count++ < 5) {
handler.postDelayed(this, 5000);
}
}
};
// trigger first time
handler.post(runnable);
Here is a simple logic I made, without moving the for loop inside runnable.
for(int i = 1; i<=5; i++){
...
new Handler().postDelayed(() -> myFunctionToExecute() , i * 1000);
}
So whenever the loop iterates, it just extends the handler delay. And this way, you may achieve. I was searching for something similar, couldn't find anything, because in my case I already did the implementation of for loop, moving it inside the run() creates a mess
My solution to this problem if anyone has simmilar issues:
int count = 0;
public static void method(param1, param2, param3) {
Runnable r = () -> { //put method inside runnable
View view = listView.getChildAt(position); //action to be complete
if (view != null) { //if action is successfully complete
view.setSelected(true); //do something with this
} else { //do a looper
if (count < 10) { //limited looper to certain number
count++;
method(param1, param2, param3); //run the method again
}
};
Handler h = new Handler(); //create a new Handler and post above thread with it
h.postDelayed(r, 300);
}
Basically, I have created an if-else statement where else statement runs the same method with postDelayed() again for a limited number of trials.
This can be another solution
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
int i;
public void run() {
for (i = 1; i < 6; i++) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
// need to do tasks on the UI thread
Log.d(TAG, "runn test");
}
}, 0);
//Add some downtime
SystemClock.sleep(5000);
}
}
};
new Thread(runnable).start();

Delay in displaying the node in JavaFx running Android

I have a Dialog class with a wait method in it to display my custom Progress Dialog:
public static void wait(String title){
isOpen = true;
ProgressIndicator progress = new ProgressIndicator(-1);
Label label = new Label(title);
label.getStyleClass().add("login-label");
HBox container = new HBox();
container.setStyle("-fx-background-color: white;");
container.setAlignment(Pos.CENTER);
container.getChildren().addAll(progress,label);
if(Main.HEIGHT < 700){
container.setSpacing(10);
container.setPadding(new Insets(10,15,10,15));
}else if(Main.HEIGHT < 1200){
container.setSpacing(15);
container.setPadding(new Insets(15,20,15,20));
}else{
container.setSpacing(20);
container.setPadding(new Insets(20,30,20,30));
}
show("", container);
}
I have this piece of code in one of my class to dislay my Progess Dialog:
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
But unfortunately there is a delay in its showing, I also tried to wrap it inside a Thread but it didn't work as well, I tried to run it in Desktop and it works perfectly but why not in my Android Device?
Here's the complete code:
download = new Button("Download");
download.getStyleClass().add("terminal-button");
download.setPrefWidth(Main.HEIGHT > 700 ? 180 : 140);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
Platform.runLater(new Runnable(){
#Override
public void run() {
//GET THE SELECTED AREAS FOR DOWNLOAD
List<String> selectedSectors = new ArrayList();
String sectorid = null;
for(Sector r : listView.getItems()){
if(r.isSelected()){
selectedSectors.add(r.getObjid());
sectorid = r.getObjid();
}
}
if(selectedSectors.size() > 1){
Dialog.hide();
Dialog.showAlert("Multiple downloads are not supported!");
return;
}
MobileDownloadService mobileSvc = new MobileDownloadService();
//INIT DOWNLOAD
Map params = new HashMap();
params.put("assigneeid", SystemPlatformFactory.getPlatform().getSystem().getUserID());
params.put("sectorid", sectorid);
batchid = mobileSvc.initForDownload(params);
int recordcount = -1;
while (true) {
int stat = mobileSvc.getBatchStatus(batchid);
if ( stat < 0 ) {
try {
Thread.sleep(2000);
}catch(Throwable t){;}
} else {
recordcount = stat;
break;
}
}
if ( recordcount <= 0 ) {
Dialog.hide();
Dialog.showError("No data to download");
return;
}
downloadsize = recordcount;
accountList = new ArrayList();
int start=0, limit=50;
while ( start < recordcount ) {
params = new HashMap();
params.put("batchid", batchid);
params.put("_start", start);
params.put("_limit", limit);
List<Map> list = mobileSvc.download(params);
//if ( list != null ) accountList.addAll( list );
System.out.println("fetch results is " + list.size());
//new Thread( new ProcessDownloadResultTask(start,list)).start();
start += limit;
}
Dialog.hide();
//SAVE AREA, STUBOUTS
clearSector();
for(Sector r : listView.getItems()){
if(r.isSelected()){
saveSector(r);
}
}
label.setVisible(true);
progressbar.setVisible(true);
progressbar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
download.setText("Cancel");
download.setDisable(false);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
continueDownload = false;
label.setVisible(false);
progressbar.setVisible(false);
download.setText("Back");
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Main.ROOT.setCenter(new Home().getLayout());
}
});
root.setOnKeyReleased(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ESCAPE){
if(Dialog.isOpen){ Dialog.hide(); return; }
Main.ROOT.setCenter(new Home().getLayout());
}
}
});
Map params = new HashMap();
params.put("batchid", batchid);
params.put("downloadedlist", downloadedList);
MobileDownloadService svc = new MobileDownloadService();
svc.cancelDownload(params);
}
});
download.setDisable(false);
}
});
}
});
The said scenario occur when you click the button, the output should be: Dialog will popup IMMEDIATELY as soon as you click the button, but sad to say, the Dialog will display after the entire process of the button was completed! I tried to wrap it in Thread but no luck!
Please help me! Any idea?
This is a short sample showing how can you use a Gluon's Dialog to handle the progress notification of a background task.
It uses a dummy task, but you can see how to handle showing and hiding the dialog, as well as using a ProgressBar to notify the progress, and even cancelling the task.
Using the Gluon Plugin for your IDE, create a Single View mobile project, and modify the view with this one:
public class BasicView extends View {
public BasicView(String name) {
super(name);
Dialog dialog = new Dialog("Download Progress");
final ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(200);
final Label label = new Label("Process has ended");
VBox vbox = new VBox(10, new Label("Download in progress..."), progressBar, label);
vbox.setAlignment(Pos.CENTER);
dialog.setContent(vbox);
final Button cancel = new Button("Cancel");
dialog.getButtons().add(cancel);
dialog.setOnShown(e -> {
cancel.setDisable(false);
label.setVisible(false);
final Task<Void> task = createDownloadTask();
progressBar.progressProperty().bind(task.progressProperty());
cancel.setOnAction(a -> task.cancel(true));
task.setOnCancelled(c -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
task.setOnSucceeded(s -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
final Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
});
Button button = new Button("Download");
button.setGraphic(new Icon(MaterialDesignIcon.CLOUD_DOWNLOAD));
button.setOnAction(e -> dialog.showAndWait());
setCenter(new StackPane(button));
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Downloads View");
}
private Task<Void> createDownloadTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
for(int i = 0; i <= 10; i++) {
if (isCancelled()) {
break;
}
try {
Thread.sleep(1000);
updateProgress(i, 10);
} catch (InterruptedException ie) {
if (isCancelled()) {
break;
}
}
}
return null;
}
};
}
}
Try replacing the dummy task with yours and see how it goes.
I solved the problem by separating its execution in one of the mouse events, instead of putting all together in the setOnAction, I placed the code Dialog.wait("Processing, please wait..."); in the setOnMousePressed, like this:
download.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
if(!Dialog.isOpen) Dialog.wait("Processing, please wait...");
}
});
download.setOnMouseReleased(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
doDownload();
}
});
This code works!

Categories