I've used the code from Caprica's old Tutorial2B.java to play the whole file:
public class Tutorial2B {
private final EmbeddedMediaPlayerComponent mediaPlayerComponent;
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Tutorial2B(args);
}
});
}
private Tutorial2B(String[] args) {
JFrame frame = new JFrame("vlcj Tutorial");
mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
frame.setContentPane(mediaPlayerComponent);
frame.setLocation(100, 100);
frame.setSize(1050, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
mediaPlayerComponent.getMediaPlayer().playMedia("/home/me/sample.MP3");
}
}
However, I cannot get the following code to play my audio file for more than a split second:
public class Tutorial2B {
private final AudioMediaPlayerComponent mediaPlayerComponent;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Tutorial2B();
}
});
}
private Tutorial2B() {
mediaPlayerComponent = new AudioMediaPlayerComponent();
MediaPlayer mediaPlayer = mediaPlayerComponent.getMediaPlayer();
mediaPlayer.playMedia("/home/me/sample.mp3")
}
Any clues as to why this isn't working?
current code Dec 21st:
public class Tutorial2B {
static AudioMediaPlayerComponent mediaPlayerComponent = null;
public static void main(String[] args) {
mediaPlayerComponent = new AudioMediaPlayerComponent();
mediaPlayerComponent.getMediaPlayer().playMedia("/home/sss.mp3");
}
}
Removed the call SwingUtilities. Plays fine now.
The reason the tutorial application works and your modified audio player application doesn't is because of garbage collection.
In both examples, when you do SwingUtilities.invokeLater() and the run() method terminates, your entire application goes out of scope and becomes eligible for garbage collection. You are not keeping a reference to the application class.
In the case of the example application this OK because creating a Swing UI (the JFrame) is enough to prevent the application from being garbage collected.
In the case of your modified application, there is no Swing UI created and consequently nothing that will prevent the application being garbage collected. In fact, in this case your application will simply exit.
I am actually surprised you say removing the use of SwingUtilities fixes your problem as there is still nothing I can see that will keep your application from being garbage collected. When I see an application like this, I have seen on Linux it will exit just about immediately, and on Windows it will exit some unpredictable time later after a garbage collection has been executed.
The more robust solution is to use something like join() on the current thread to prevent the application from exiting, or somehow otherwise keep a reference to your application class pinned, and then for example to wait for a media player event of "finished" or "error" before terminating your application.
In "real" applications this sort of thing is generally not a concern since there are other things keeping your application from being garbage collected (like a UI, or some other application framework).
Related
I have a function to change an image and its opacity in a JavaFX GUI:
private static Image image = null;
private static ImageView imageView = new ImageView();
// some code to add image in GUI
public static void changeImage() {
imageView.setOpacity(0.5);
imageView.setImage(null);
}
When I call this function within the JavaFX instance, the image disappears or is changing if I use an image instead of null for setImage(). I tried calling the function by pressing a button.
In this case all works as I expected.
When I call this function from another class, the actual image will change its opacity, but the image itself is never changing. I call the function the following way:
public static void changeImg() {
Platform.runLater(() -> FX_Gui.changeImage());
}
Changing labels, progess bars... all works, but I did not manage to change an image.
There's a lot of aspects to this question that don't make sense.
Generally speaking, the GUI in JavaFX is intended to be self-contained and non-linear in it's execution. Programming an outside method to assume some state of the GUI, and then to directly manipulate the GUI based on that assumption is not the correct approach. So any attempt to know the state of the GUI by kludging in a Thread.sleep() call is inherently incorrect.
The new JFXPanel() call is not needed, as Application.launch() will initialize JavaFX. Presumably, this was added before the sleep(500) was put in, since calling changeImg() would fail if run immediately after the Thread.start() command, since the launch() wouldn't have time to even start yet.
As has been noted, having some kind of startup image that's replaced once the screen completes initialization should be done from within the FX_Min.start(Stage) method, although it's highly unlikely that you'd even see the first image.
The question seems to be aimed at designing a kind of application where the GUI is just some small part of it and the main application is going to go on to do lengthy processing and then trigger the GUI to something in response to the results of that processing. Or perhaps the main application is monitoring an external API and feeding updates to GUI periodically. In most cases, however, the GUI is usually initialized so that it can take control of the operation, launching background threads to do the lengthy processing and using JavaFX tools to handle the triggering of GUI updates and intake of results.
In the instance that the design really needs to have something other than the GUI be the central control, then use of Application does not seem appropriate. It is, after all, designed to control the Application, and monitors the status of the GUI once it's been launched to shut everything down when the GUI is closed. This is why the OP had to put the Application.launch() call in a separate thread - launch() doesn't return until the GUI shuts down.
If the application outside of the GUI is going to control everything then it's best to manually start JavaFX with Platform.startup(), and handle all the monitoring manually. The following code doesn't do any monitoring, but it does start up the GUI and change the image without any issues:
public class Control_Min {
public static void main(String[] args) {
Platform.startup(() -> new Fx_Min().start(new Stage()));
Platform.runLater(() -> Fx_Min.changeImage());
}
}
Note that no changes are required to the OP's code in Fx_Min. However, there's no reason for Fx_Min to extend Application any more, and the code from its start() method can be placed anywhere.
It should be further noted that, although this works, it's really way outside the norm for JavaFX applications. It's possible that the OP's situation really does require this kind of architecture, but that would place it into a very small minority of applications. Designing the application around Application.launch() and initiating lengthy processing in background threads through the JavaFX tools provided is almost always a better approach.
OK, so given new information from the OP it's clear that this should be based on Application and that the GUI should launch some kind of socket listener that would presumably block waiting for input.
Anything that blocks can't run on the FXAT, and there needs to be a way to allow the socket listener to communicate back to the GUI when it receives data. Ideally, the socket listener should be JavaFX unaware, and just plain Java.
IMO, the best way to do this is to provide a Consumer to accept information from the socket listener, and to pass it to the socket listener in it's constructor. That way, the GUI knows nothing about the nature of the socket listener except that it has a dependency on requiring a message consumer. Similarly, the socket listener has no knowledge about what invoked it, just that it has given it a message consumer.
This limits your coupling, and you are free to write your GUI without worrying about any of the inner workings of the socket listener, and visa versa.
So here's the GUI, cleaned up and simplified a bit so that the socket listener stuff is easier to follow. Basically, the GUI is just going to throw the message from the socket listener into a Text already on the screen. The message consumer handles the Platform.runLater() so that the socket listener isn't even aware of it:
public class Fx_Min extends Application {
#Override
public void start(Stage primaryStage) {
ImageView imageView = new ImageView(new Image("/images/ArrowUp.png"));
Text text = new Text("");
primaryStage.setScene(new Scene(new VBox(10, imageView, text), 800, 600));
primaryStage.setResizable(true);
primaryStage.show();
imageView.setImage(new Image("/images/Flag.png"));
new SocketListener(socketMessage -> Platform.runLater(() -> text.setText(socketMessage))).startListening();
}
public static void main(String[] args) {
launch(args);
}
}
Here's the socket listener. Clearly, this isn't going to listen on a socket, but it loops around a sleep() to simulate action happening on the Pi. The message format here is String, just to keep everything simple, but obviously this is the worse possible choice for an actual implementation of this. Build a special message class:
public class SocketListener {
private Consumer<String> messageConsumer;
public SocketListener(Consumer<String> messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void startListening() {
Thread listenerThread = new Thread(() -> listenForIRCommand());
listenerThread.setDaemon(true);
listenerThread.start();
}
private void listenForIRCommand() {
for (int x = 0; x < 100; x++) {
try {
Thread.sleep(5000);
messageConsumer.accept("Station " + x);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
It should be really clear that since the call to listenForIRCommand() is executed from inside a background thread, that it's completely freed from any JavaFX contstraints. Anything that generally possible in Java can be done from there without worrying about it's impact on the GUI.
In the meantime I found out that the reason for not changing the image is that I run changeImage() before the initialization of the GUI is completed. If I wait about 500 mS before I sent the changeImage() command all works fine.
Below is the minimal code that demonstrates the issue I had:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
public class Control_Min {
public static void changeImg() {
Platform.runLater(() -> Fx_Min.changeImage());
}
public static void main(String[] args) {
new Thread() {
public void run() {
Application.launch(Fx_Min.class);
}
}.start();
// JFXPanel will initialize the JavaFX toolkit.
new JFXPanel();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
changeImg();
}
}
And the Gui itself:
public class Fx_Min extends Application {
private static Stage stage;
private static GridPane rootPane;
private static Scene scene;
private static Image image = null;
private static ImageView imageView = new ImageView();
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
rootPane = new GridPane();
scene = new Scene(rootPane,800,600);
try {
image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image1.jpg"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
imageView.setImage(image);
rootPane.add(imageView, 1, 0);
stage.setScene(scene);
stage.setResizable(true);
stage.show();
System.out.println("Gui is ready");
}
public static void changeImage() {
try {
image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image2.jpg"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
imageView.setImage(image);
System.out.println("Image Changed");
}
public static void main(String[] args) {
launch(args);
}
}
This code works fine.
In the console I get:
Gui is ready
Image Changed
When I remove the Thread.sleep(500) the image will not change.
In the console I get:
Image Change
Gui is ready
My conclusion is that I have send the runlater method before the FX runtime has been initialized.
(Have not fixed the static issue yet as this was not the issue. I will do in my original program later.)
My task is the following:
I program a GUI for my internet radio player on my PC.
The GUI controls the radio and polls what is playing.
I want to control the radio by an IR remote control too.
I have already a Raspberry Pi that communicates with the remote.
Therefore, my plan is to run a server socket on the PC, that receives the commands from the Raspberry Pi.
The server will run in its own thread. I want to use the runLater command to update the GUI.
Is there a better way to update the GUI from the server?
Goal is that the GUI will update immediately when I press a button on the remote.
With my latest learnings about JavaFX I will start the application now directly in the FX class and get the server thread started from the FX class
When opening a new dialog, while its loading, you click couple of times on parent shell, apparently the new dialog does not display correctly.
Please see the example below:
Examples
https://i.stack.imgur.com/ZovxE.png (eclipse IDE example)
https://i.stack.imgur.com/5zVar.png
https://i.stack.imgur.com/u86b9.png
https://i.stack.imgur.com/FGaAr.png
Initially I encountered the problem in december 2014, and back then also reported by vaious in house devlopers which were using different development systems and then same problem has been reported by our several customers.
This behavior can be reproduced using following environment:
Windows Version: 7 Pro 64 Bit - 6.1.7601
Java Version: RE 1.8.0_121_b13
SWT Versions
3.8.2
4.6.2
4.7M6
I20170319-2000
I could only reproduce the problem on Windows 7 with the windows basic theme/design/style (not with classic or aero).
On windows 10 its not reproducible.
reproduce
code to reproduce
package test;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class Main {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = createShell(display);
createButton(shell);
shell.open();
eventLoop(display, shell);
display.dispose();
}
private static Shell createShell(Display display) {
final Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
shell.setSize(500, 200);
return shell;
}
private static void createButton(final Shell shell) {
final Button openDialog = new Button(shell, SWT.PUSH);
openDialog.setText("Click here to open Dialog ...");
openDialog.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TestDialog inputDialog = new TestDialog(shell);
inputDialog.open();
}
});
}
private static void eventLoop(Display display, final Shell shell) {
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
class TestDialog extends Dialog {
public TestDialog(Shell parent) {
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.MAX | SWT.RESIZE);
setText("Dialog");
}
public void open() {
Shell shell = new Shell(getParent(), getStyle());
shell.setText(getText());
createContents(shell);
shell.pack();
initializeBounds(shell);
shell.open();
eventLoop(shell);
}
private void createContents(final Shell shell) {
shell.setLayout(new GridLayout(2, true));
Label label = new Label(shell, SWT.NONE);
label.setText("Some Label text ...");
final Text text = new Text(shell, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
text.setLayoutData(data);
createCloseButton(shell);
/* time for the user to create the misbehavior */
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createCloseButton(final Shell shell) {
Button closeButton = new Button(shell, SWT.PUSH);
closeButton.setText("Close");
GridData data = new GridData(GridData.FILL_HORIZONTAL);
closeButton.setLayoutData(data);
closeButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
shell.close();
}
});
shell.setDefaultButton(closeButton);
}
private void initializeBounds(Shell shell) {
Rectangle bounds = shell.getBounds();
Rectangle parentBounds = getParent().getBounds();
bounds.x = parentBounds.x;
bounds.y = parentBounds.y;
shell.setBounds(bounds);
}
private void eventLoop(Shell shell) {
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
steps to reproduce
Start the application
it should look like: https://i.stack.imgur.com/dMJ9e.png
Click on the button.
Keep continuously clicking on right bottom corner of the parent shell (avoid hitting the new opening dialog), till mouse cursor changes to wait icon and parent shell changes its color.
it should look as following: https://i.stack.imgur.com/c1Ikp.png
Wait until the new dialog appears.
it looks likes as following: https://i.stack.imgur.com/kTDgQ.png (incorrectly displayed)
instead: https://i.stack.imgur.com/cHVjn.png (correctly displayed)
steps to reproduce done in video
https://youtu.be/7ukhloCPf0k
When you mouse hover some of the UI elements (the originally not correctly drawn), you can notice some of them to be get painted (e.g. table rows).
https://i.stack.imgur.com/kkMKn.png (before opening the dialog)
https://i.stack.imgur.com/ZXIKc.png (after opening the dialog)
https://i.stack.imgur.com/25M7S.jpg (after mouse over)
Even calling Shell.update() or Shell.redraw() after the Dialog opened does not fix it.
In Windows Performance Options -> Visual Effects -> disable "Use visual styles on windows and buttons" is the only option I found which provides a workaround,
which seems to be the same as changing the design/theme/style to classic.
https://www.sevenforums.com/tutorials/1908-visual-effects-settings-change.html (How to Change Windows Visual Effects)
In the end, I have following questions:
Is it a SWT or Windows problem?
Is there any related topic in bug entries for Windows or in Eclipse Bugzilla?
Is there someone else who experienced the same problem? please share the experience.
Is there any settings in SWT or Windows which could affect its look n feel and fix the problem?
In the end, I have following questions: Is it a SWT or Windows problem?
Neither. As others have mentioned, you certainly should not tie up the UI thread with any long-running task. That work belongs in a background thread.
In regards to using a background thread, there are several ways you could go about this depending on how you want your Dialog to behave.
One option would be to kick off the background thread and then open the dialog when the task is done. I personally don't care for this because while the task is running, a user may think that nothing is happening.
Another option would be to open the dialog but display a "Loading" message, or something to that effect to give meaningful feedback and let a user know that the application isn't frozen (like how it looks/responds in your example).
The strategy would be to:
Create the dialog
Start the long task on a background thread and register a callback
Open the dialog with a "Loading" message
When the task is complete, the dialog will be updated from the callback
If you search around a bit on using Executors, you should find some far better examples and detail on how to use them.
Here's a brief example to illustrate what that might look like:
(Note: There are definitely a few issues with this code, but for the sake of brevity and illustrating the point I opted for a slightly naive solution. Also there are Java 8-esque ways that would be a bit shorter, but again, this illustrates the idea behind using a background thread; the same concepts apply)
Given a Callable (or Runnable if you don't need a return value),
public class LongTask implements Callable<String> {
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
You can use the Executors class to create a thread pool, and then an ExecutorService to submit the Callable for execution. Then, using Futures.addCallback(), you can register a callback which will execute one of two methods depending on whether the task was successful or failed.
final ExecutorService threadPool = Executors.newFixedThreadPool(1);
final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPool);
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new FutureCallback(){...});
In this case I used the Google Guava implementation ListeningExecutorService which makes things a bit cleaner and simpler, in my opinion. But again, you may not even need this if you opt for a more "Java 8" approach.
As for the callback, when the task is successful, we update the Dialog with the results. If it fails, we can update it with something to indicate failure:
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
In this case I opted for the Callable to return a String, thus the FutureCallback should be parameterized with String. You may want to use some other class that you created, which will work just as well.
Notice that we use the Display.asyncExec() method to ensure that the code which updates the UI runs on the UI thread, because the callback may execute on the background thread.
Like I said, there are still a few issues here, including what happens when you click the cancel button before the task completes, etc. But hopefully this helps illustrate an approach for handling long-running background tasks without blocking the UI thread.
Full example code:
public class DialogTaskExample {
private final Display display;
private final Shell shell;
private final ListeningExecutorService executorService;
public DialogTaskExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
final Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
button.setText("Start");
button.addSelectionListener(new SelectionAdapter() {
#SuppressWarnings("synthetic-access")
#Override
public void widgetSelected(final SelectionEvent e) {
final MyDialog dialog = new MyDialog(shell);
dialog.setBlockOnOpen(false);
dialog.open();
dialog.setStatus("Doing stuff...");
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new DialogCallback(dialog));
}
});
}
public void run() {
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
executorService.shutdown();
display.dispose();
}
public static void main(final String... args) {
new DialogTaskExample().run();
}
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
public static class LongTask implements Callable<String> {
/**
* {#inheritDoc}
*/
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
public static class MyDialog extends Dialog {
private Composite baseComposite;
private Label label;
/**
* #param parentShell
*/
protected MyDialog(final Shell parentShell) {
super(parentShell);
}
/**
* {#inheritDoc}
*/
#Override
protected Control createDialogArea(final Composite parent) {
baseComposite = (Composite) super.createDialogArea(parent);
label = new Label(baseComposite, SWT.NONE);
return baseComposite;
}
public void setStatus(final String text) {
label.setText(text);
baseComposite.layout();
}
}
}
The code seems to be straight forward, only that you are making the main Thread sleep for 15secs hence the delay. If not required remove the sleep or reduce the time for sleep to 5secs or so.
I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
bootStrapper.start();
}
}
#Component
public class AppBootStrapper {
private NetworkScanner networkScanner;
private PacketConsumer packetConsumer;
public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
this.networkScanner = networkScanner;
this.packetConsumer = packetConsumer;
}
public void start() {
networkScanner.addConsumer(packetConsumer::consumePacket);
networkScanner.startScan();
}
}
#Component
public class NetworkScanner {
private List<Consumer<String>> consumers = new ArrayList<>();
public void startScan(){
Executors.newSingleThreadExecutor().submit(() -> {
while(true) {
// do some scanning and get/parse packets
consumers.forEach(consumer -> consumer.accept("Package Data"));
}
});
}
public void addConsumer(Consumer<String> consumer) {
this.consumers.add(consumer);
}
}
#Component
public class PacketConsumer {
public void consumePacket(String packet) {
System.out.println("Packet received: " + packet);
}
}
#RunWith(JUnit4.class)
public class AppBootStrapperTest {
#Test
public void start() throws Exception {
NetworkScanner networkScanner = mock(NetworkScanner.class);
PacketConsumer packetConsumer = mock(PacketConsumer.class);
AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);
appBootStrapper.start();
verify(networkScanner).addConsumer(packetConsumer::consumePacket);
verify(networkScanner, times(1)).startScan();
}
}
I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:
Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546#282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapper$$Lambda$7/920446957#5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)
From the exception, clearly the function pointers aren't the same.
Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.
Any help, perspectives on this would be greatly appreciated.
Java doesn't have any concept of "function pointers"; when you see:
networkScanner.addConsumer(packetConsumer::consumePacket);
What Java actually compiles is (the equivalent of):
networkScanner.addConsumer(new Consumer<String>() {
#Override void accept(String packet) {
packetConsumer.consumePacket(packet);
}
});
This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.
If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.
A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.
See the comments and answer on this SO question.
I am using PostSharp to log performance and other statistics on some methods. I was asked to measure the performance and time taken on some sub tasks, such as calling an external web service, or a large database, etc.
For example, I have a method with the AoPLoggingAttribute applied. AoPLoggingAttribute inherits from OnMethodBoundaryAspect, so it supports all know methods (OnEntry, OnExit, OnSuccess, etc)
[AoPLogging]
public MyClass[] MyMainMethod(string myid)
{
//Some code here
LongExecutingTask();
//Rest of the code here
}
What is the best approach to measure the time taken by LongExecutingTask ? I don't care if it's part of the total executing time, but somehow I need to know the time taken from this task.
If you want to use postsharp you could make a timer aspect like this
public class TimingAttribute : OnMethodBoundaryAspect
{
Stopwatch timer = new Stopwatch();
public override void OnEntry(MethodExecutionArgs args)
{
timer.Reset();
timer.Start();
base.OnEntry(args);
}
public override void OnExit(MethodExecutionArgs args)
{
timer.Stop();
Console.WriteLine("Execution took {0} milli-seconds", timer.ElapsedMilliseconds);
base.OnExit(args);
}
}
Now just attach the aspect to the method you want to time
[Timing]
public void LongExecutingTask() {}
Remember that postsharp, or AOP in general, works by attaching to the method being called. Not by adding code insert your main method (or whereever you are calling the methods)
Update: If you really want to keep track of the whole callstack you could do something like this
public class TimingAttribute : OnMethodBoundaryAspect
{
static List<Stopwatch> callstack = new List<Stopwatch>();
static int callstackDepth = 0;
public override void OnEntry(MethodExecutionArgs args)
{
var timer = new Stopwatch();
timer.Start();
callstack.Add(timer);
++callstackDepth;
base.OnEntry(args);
}
public override void OnExit(MethodExecutionArgs args)
{
--callstackDepth;
var timer = callstack[callstackDepth];
timer.Stop();
if (callstackDepth == 0) {
//Add code to print out all the results
Console.WriteLine("Execution took {0} milli-seconds", timer.ElapsedMilliseconds);
callstack.Clear();
}
base.OnExit(args);
}
}
Now this only works with 1 single callstack. If you were to have 2 LongExecutingTasks in your main method you would have to think about how you want to report over thoes. But maybe this gives you an idea how you could keep track of the whole callstack.
You must assign your timer to the MethodExecutionArgs in order to get accurate results in a multi-threaded environment. PostSharp internally assigns to a static class, so any members risk being overwritten by concurrent invocations.
public class TimingAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
args.MethodExecutionTag = Stopwatch.StartNew();
}
public override void OnExit(MethodExecutionArgs args)
{
var sw = (Stopwatch)args.MethodExecutionTag;
sw.Stop();
System.Diagnostics.Debug.WriteLine("{0} executed in {1} seconds", args.Method.Name,
sw.ElapsedMilliseconds / 1000);
}
}
In the viewpoint of running code in the UI thread, is there any difference between:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
or
MainActivity.this.myView.post(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
and
private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
protected void onPostExecute(Bitmap result) {
Log.d("UI thread", "I am the UI thread");
}
}
None of those are precisely the same, though they will all have the same net effect.
The difference between the first and the second is that if you happen to be on the main application thread when executing the code, the first one (runOnUiThread()) will execute the Runnable immediately. The second one (post()) always puts the Runnable at the end of the event queue, even if you are already on the main application thread.
The third one, assuming you create and execute an instance of BackgroundTask, will waste a lot of time grabbing a thread out of the thread pool, to execute a default no-op doInBackground(), before eventually doing what amounts to a post(). This is by far the least efficient of the three. Use AsyncTask if you actually have work to do in a background thread, not just for the use of onPostExecute().
I like the one from HPP comment, it can be used anywhere without any parameter:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
There is a fourth way using Handler
new Handler().post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
The answer by Pomber is acceptable, however I'm not a big fan of creating new objects repeatedly. The best solutions are always the ones that try to mitigate memory hog. Yes, there is auto garbage collection but memory conservation in a mobile device falls within the confines of best practice.
The code below updates a TextView in a service.
TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
private String txt;
#Override
public void run() {
searchResultTextView.setText(txt);
}
public void setText(String txt){
this.txt = txt;
}
}
It can be used from anywhere like this:
textViewUpdater.setText("Hello");
textViewUpdaterHandler.post(textViewUpdater);
As of Android P you can use getMainExecutor():
getMainExecutor().execute(new Runnable() {
#Override public void run() {
// Code will run on the main thread
}
});
From the Android developer docs:
Return an Executor that will run enqueued tasks on the main thread associated with this context. This is the thread used to dispatch calls to application components (activities, services, etc).
From the CommonsBlog:
You can call getMainExecutor() on Context to get an Executor that will execute its jobs on the main application thread. There are other ways of accomplishing this, using Looper and a custom Executor implementation, but this is simpler.
If you need to use in Fragment you should use
private Context context;
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
((MainActivity)context).runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
instead of
getActivity().runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
Because There will be null pointer exception in some situation like pager fragment
use Handler
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
Kotlin version:
Handler(Looper.getMainLooper()).post {
Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}
Or if you are using Kotlin coroutines:
inside coroutine scope add this:
withContext(Dispatchers.Main) {
Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}