I'm currently playing around with the Evaluation License for JxBrowser 6.2.
I'm creating the BrowserView as follows:
Browser browser = new Browser(BrowserType.HEAVYWEIGHT);
BrowserView browser_view = new BrowserView(browser);
I'm attaching the BrowserView component as follows:
stage.setScene(new Scene(browser_view));
If the Browser is configured to operate in LIGHTWEIGHT mode, I'm able to execute:
browser_view.getBrowser().dispose();
Platform.exit();
However, if the Browser is configured to operate in HEAVYWEIGHT mode, then the application hangs when executing:
browser_view.getBrowser().dispose();
I can see in the logs that the Dispose message was written, but it appears as though the JxBrowser Chromium processes never receive/process the message.
Any ideas?
As answered before me the solution to this is to dispose the browser after the stage has been hidden (closed).
A good approach would be to put those commands on the stop() method of JavaFX Application.
So that either way you close the window (by clicking the close button or programmatically by calling Platform.exit()), the browser will dispose (and the whole application will finish and exit).
Something like that:
#Override
public void stop() throws Exception {
stage.hide();
browser.dispose();
}
As a reference, I used configuration described here link (Section: 9. Pop-up Windows).
Platform.runLater(() -> {
browser.dispose();
});
Platform.runLater(() -> {
stage.close();
});
It looks like you need to ensure that the stage has been closed before calling dispose.
stage.close();
browser_view.getBrowser().dispose();
Platform.exit();
Please try calling this code asynchronously. Maybe it's just a deadlock:
Platform.runLater(new Runnable() {
#Override
public void run() {
browser_view.getBrowser().dispose();
Platform.exit();
}
});
Related
I've been looking for ages for direction on this matter and I finally post here.
I have a JavaFX application with MediaPlayer. One day, seeking at a later position in video (that had not been accessed previously) started hanging the player. No status change before it gets to PLAYING, the buffer is loaded, status at READY before I call seek().
First I thought it is because I went out of Application thread, tried to put the MediaPlayer back on the root to be sure, and the seek method worked as before, fast enough for me.
But then for a reason I can't get, it started hanging again all the time, with same symptoms.
Now, even with the most simple code, it hangs too.
I'm desperate, the waiting time can be 30 seconds to reach a position 2 minutes later in the video. Looks like the Media Player is scanning again all video until it finds the good position it's seeking, thus taking more time for a later position. If the position has been accessed before though, seek() won't hang...
Am I the only one with this problem?
I'm on Mac os EL Capitan, but tried on Windows VM too and I get the same behaviour.
Here is a standalone code, but I don't see how it will help, I don't even hope for ppl to reproduce:
public class VideoPlayerExample extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
File file = new FileChooser().showOpenDialog(stage);
Media media = new Media(file.toURI().toString());
MediaPlayer mp = new MediaPlayer(media);
mp.statusProperty().addListener(new ChangeListener<MediaPlayer.Status>() {
#Override
public void changed(ObservableValue<? extends Status> observable, Status oldValue, Status newValue) {
System.out.println(newValue);
}
});
Group gp = new Group(new MediaView(mp));
Button buttonTest = new Button("It's gonna hang...");
buttonTest.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
mp.pause();
System.out.println(mp.getCurrentTime().toMillis());
mp.seek(new Duration(mp.getCurrentTime().toMillis() +10000));
mp.play();
}
});
gp.getChildren().add(buttonTest);
stage.setScene(new Scene(gp, 540, 208));
stage.show();
}
}
Any help will be so greatly appreciated!
You're right - I can't reproduce your problem. I have macOS Sierra 10.12.6. All I can say is check the type of movie you're trying to play - not all encodings are supported. Also, according to the documentation, if the movie's duration is Duration.INDEFINITE, seek() will have no effect.
Place the seek method in a new thread not on your JavaFX thread.
new Thread(() -> mp.seek(new Duration(mp.getCurrentTime().toMillis() +10000)))).start();
I would understand why
scene.setCursor(Cursor.WAIT);
long task...
scene.setCursor(Cursor.DEFAULT);
needs new threads; it works with:
private void set_cursore_attesa(final Scene scene)
{
Runnable r=new Runnable() {
#Override
public void run() {
scene.setCursor(Cursor.WAIT);
}
};
Thread t=new Thread(r);
t.start();
}
private void set_cursore_normale(final Scene scene)
{
Runnable r=new Runnable() {
#Override
public void run() {
scene.setCursor(Cursor.DEFAULT);
}
};
Thread t=new Thread(r);
t.start();
}
in my function:
set_cursore_attesa(scene);
long task...
set_cursore_normale(scene);
why I can't use the same thread?
I:
set my cursor to WAIT (it goes in GUI queue)
do my long task... (it goes in GUI queue but I expected that cursor changing, that is up in queue, it is executed before this)
reset my cursor to DEFAULT (after my task has finished)
So, my long task doesn't go in MAIN queue? because, if it goes in main queue, I expected it's executed after my WAIT cursor that is inserted in queue first.
Why this behavior?
Without the threads, your code is being executed on the FX Application Thread. This is the thread that is (effectively) responsible for rendering the UI to the screen and for processing user input. If you execute a long-running task on this thread, then you prevent any of the normal functionality of the FX Application Thread from occurring until your long-running task is complete. In particular, if you do
scene.setCursor(Cursor.WAIT);
longRunningTask();
scene.setCursor(Cursor.DEFAULT);
then the settings take place in the order you specify, but the scene does not get rerendered until all lines of code are complete. Hence you never actually see any changes to the UI - including the change to the cursor - until after your code is complete. The next time the FX Application Thread has an opportunity to render the scene, the cursor is set to Cursor.DEFAULT, and you never see the wait cursor.
There are two basic rules for multithreading and JavaFX (and the same rules generally apply to most UI toolkits):
Any changes to the UI must be performed on the FX Application Thread
Long-running processes should not be performed on the FX Application Thread (as they make the UI unresponsive)
So your solution is not actually correct, because you violate both of those rules. You should
Set the cursor to WAIT on the FX Application Thread
Start your long running task on a background thread
When the task is complete, set the cursor back to DEFAULT, on the FX Application Thread.
You can do this using a Task:
scene.setCursor(Cursor.WAIT);
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
// long running task here...
return null ;
}
};
task.setOnSucceeded(e -> scene.setCursor(Cursor.DEFAULT));
new Thread(task).start();
I'm trying to get a responsive JavaFX graphical interface while executing a cmd command.
The command I'm executing is the following.
youtube-dl.exe --audio-format mp3 --extract-audio https://www.youtube.com/watch?v=l2vy6pJSo9c
As you see this is a youtube-downloader that converts a youtube link to an mp3-file.
I want this to be executed in a second thread and not in the main FX thread.
I've solved this by implementing interface Callable in the class StartDownloadingThread.
#Override
public Process call() throws Exception {
Process p = null;
p = ExecuteCommand(localCPara1, localCPara2, localDirectory).start();
try {
Thread.sleep(30);
}catch (InterruptedException e){}
return p;
}
The method ExecuteCommand just returns a ProcessBuilder object.
I try to use Thread.sleep to make the program return to the main thread and thus making the application responsive. Unfortunately the program still freezes.
This is how the method call is called.
ExecutorService pool = Executors.newFixedThreadPool(2);
StartDownloadingThread callable = new StartDownloadingThread(parameter1, parameter2, directory);
Future future = pool.submit(callable);
Process p = (Process) future.get();
p.waitFor();
How do I make my GUI responsive using the interface Callable?
Using a executor to run a task just for you to use the get method of the Future that is returned when submitting the task does not actually free the original thread to continue with other tasks. Later you even use the waitFor method on the original thread, which is likely to take even more time than anything you do in your Callable.
For this purpose the Task class may be better suited, since it allows you to handle success/failure on the application thread using event handlers.
Also please make sure an ExecutorService is shut down after you're done submitting tasks.
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
Process p = null;
p = ExecuteCommand(localCPara1, localCPara2, localDirectory).start();
// why are you even doing this?
try {
Thread.sleep(30);
}catch (InterruptedException e){}
// do the rest of the long running things
p.waitFor();
return null;
}
};
task.setOnSucceeded(event -> {
// modify ui to show success
});
task.setOnFailed(event -> {
// modify ui to show failure
});
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(task);
// add more tasks...
// shutdown the pool not keep the jvm alive because of the pool
pool.shutdown();
I am using JxBrowser 6.1.1 and succeed in registering a Java object in the JsContext in a onFinishLoadingFrame handler. The problem is that the property is not yet available when the first JavaScript is run in the page loaded by the JxBrowser.
The working call looks like this:
browser.executeJavaScriptAndReturnValue("window").asObject().setProperty("api", api);
When I do the same in the new onScriptContextCreated handler, the program halts executing:
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
The full code is and fails at line JSValue jsWindow = ...:
client.browser.addScriptContextListener(new ScriptContextAdapter() {
#Override
public void onScriptContextCreated(ScriptContextEvent event) {
Browser browser = event.getBrowser();
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
JSObject windowObject = jsWindow.asObject();
windowObject.setProperty("api", client.getApi());
}
});
I guess the window property is simply not there yet.
Is there a way to achieve this with the onScriptContextCreated event or is there a different way to achieve this?
The code also works fine when started in a separate thread as proposed below. But the problem remains that this code runs later than the first JavaScript in the browser.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
JSObject windowObject = jsWindow.asObject();
windowObject.setProperty("api", client.getApi());
}
});
thread.start();
I got some operations in my Controller class which could take some time. So I want to show a loading dialog while this operation is running.
I tried this:
Platform.runLater(new Runnable() {
#Override
public void run() {
loadingDialog.show();
}
});
Boolean opSuccess = myService.operate();
Platform.runLater(new Runnable() {
#Override
public void run() {
loadingDialog.hide();
}
});
if (opSuccess) {
// continue
}
Now, the Problem is, the loadingDialog is never show. The UI only blocks for some time and than continues on "//continue".
So it seems, the runLater call is blocked by the blocking operation (operate)?
I also tried CoundDownLatch, to wait for loadingDialog.show() to complete, before running myService.operate(). But the latch.await() method never completes.
So my question is, how my I show the loadingDialog until myService.operate() finished and returned true or false? Do I have to put the operate() call into another thread and run it async or is there an easier way?
Thanks for help.
Are you sure your entire code does not run in the JavaFX Thread?
Methods of your controller class usually do and I assume it due to your description.
However, better use the Task class. Here you'll find a tutorial and a short snippet for your application:
// here runs the JavaFX thread
// Boolean as generic parameter since you want to return it
Task<Boolean> task = new Task<Boolean>() {
#Override public Boolean call() {
// do your operation in here
return myService.operate();
}
};
task.setOnRunning((e) -> loadingDialog.show());
task.setOnSucceeded((e) -> {
loadingDialog.hide();
Boolean returnValue = task.get();
// process return value again in JavaFX thread
});
task.setOnFailed((e) -> {
// eventual error handling by catching exceptions from task.get()
});
new Thread(task).start();
I assumed Java 8 and the possibility to use Lambda expressions. Of course it is possible without them.
You are better off making use of concurrency mechanisms/Worker interfaces in JavaFx - Tasks and services instead of using Platform.runLater(). The tasks and services allow you to manage the long running tasks in a separate thread. They also provide callbacks to indicate the progress of the tasks.
You could explore further at http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
Also have a look at the Ensemble JavaFX samples for Tasks and Services - http://www.oracle.com/technetwork/java/javase/overview/javafx-samples-2158687.html