Hardly, do I understand multithreading stuff. And beginning look into it , I faced one issue, that came in my mind. I have recently wrote one simple application, and as soon as I get some new knowledge about Java I wish to improve my app with that I’ve learned.
It looks like simple swing GUI that updates images every period of time. I implemented ActionListener and overrode actionPerformed method. Timer with 15ms delay, repainted JPanelclass and everything worked fine. But I thought that updating my GUI using timer directly in actionPerformed (I presume that it’s another thread, but I’m barely sure) is bad idea. So I decided to change code and use SwingWorker. I called all my methods for my animation inside process() .. and again app working fine but it became extremely slow.
Now I’m thinking what’s wrong? Why it acts slower then before ? My timer delay is actually not waiting 15ms , it’s much slower even though delay the same. Am I made mistake with multithreading ?
Help me understand this stuff. Thanks in advance
public class GameEngine() extends SwingWorker<Void, Drawable>
GamePanel gp; // ref to JPanel class
{
public GameEngine(GamePanel gp)
{
this.gp = gp;
}
}
protected void doInBackground()
{
publish();
}
protected void process(List<Drawable> chunks)
{
Timer timer = new Timer(15, e ->
{
//methods for animation
fall();
animate();
checkTouch();
});
}
Some code I left beyond. If you need it I can write...
EDITION
Just for clarity of my issue I provide some more examples and addition explanation.
**Used to be: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () implements ActionListener
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
timer = new Timer( 15 , this );
}
public void actionPerformed()
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
}
**Became: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () extends SwingWorker<Void, Drawable>
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
}
protected void doInBackground()
{
process();
}
protected void progress()
{
timer = new Timer (15, e->
{
new ActionListener(new actionPerformed)
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
});
}
protected void done()
{
};
}
When I created it first I implemented ActionListener and updated my panel through timer declared in constructor.. I presumed that it’s thread-unsafe.
That’s why I transfer everything in progress method where I declared timer which ActionListener as lambda argument.
In other words I call all methods for animation in another thread.
Finally it became slower, comparing with first example..
I don’t understand
Is Timer from first example EDT or it’s another thread ?
My first example is thread safe ?
Why my second example goes much slower then first one ?
I heard about NOT update your GUI outside EDT, is it that case?
There is not enough information in your question to answer it, but I will take your queries about multi-threading and the implied question about Swing and rendering and see if I can help you out.
I think the most likely reason for your slowdown is unnecessary screen updating. Once a pixel has been drawn onto the canvas or whatever in your application, usually your application does not need to redraw it unless it is supposed to change; either a sprite moves or some other image in your app obscures a part of the drawing temporarily and then needs to be restored.
It is common for a novice repainting to ignore this, and just repaintng the entire painted surface. Although this will work, it is slow; if you're doing it a number of times in a loop, then the loop will seem slow.
The better way to do this is to use the rectangle passed into the redrawing routine and only repaint its intersection with the entire surface redrawn by your routine -- this cuts way down on the part that needs to be redrawn, and therefore on the time it takes to redraw it.
As for multithreading, I think it's helpful to think of it the way we used to think of things in a single-processor world -- the computer does something for a while, then stops in a place you cannot predict and does something in the other thread for a while, etc. You cannot assume the order in which things are going to get done, or how long it will spend on each thing, etc.
With modern multi-core computers, it is possible that these things in fact get done at the same time, but I don't know that trying to envision that helps you any.
Related
I'm new to JAVA and trying to learn some concurrency concepts.
I have a simple GUI class that pops-up a window with 1 button which I want to use for pause/continue.
Also, I have a class that extends TimerTask, it looks like below and start with the GUI:
public class process extends TimerTask {
public void run() {
while(true) { /*some repetitive macro commands.. */ }
}
}
Real question is, how can I pause the task onClick of the button and also continue onClick of the button if already paused?
I have taken a step to use a boolean to flag the button as it changes from pause to continue on each click.
But then I had to type a lot of while(button); for busy waiting inside the while()...
Do you think I can make like Thread.sleep() or something but from outside the task thread?
OLD ANSWER
Basically, there is no support for pause and resume on TimerTask, you can only cancel, check here
perhaps you might want to read about threading as that's the alternative I know of that has an interrupt and start features and then you can keep track of the progress of what you're doing to resume where it stopped.
So, I will suggest you go through this link, because you need to understand threading not just copy a code to use, there is a sample code there that will definitely solve your problem also.
Note that running an endless while loop will basically cause your program not to respond, unless the system crashes. At a certain point, the data becomes an overload and the program will overflow. This means it will fail.
.
NEW ANSWER
So, response to the new question, I was able to run a tiny little program to demonstrate how you can achieve something that looks like multithreading when working with SWING.
To rephrase your question: You want to run an indefinite task like let say we're playing a song, and then onclick of a button to pause the song, on click again should continue the song?, if so, I think below tiny program might work for you.
public class Test{
static JLabel label;
static int i = 0;
static JButton action;
static boolean x = false; //setting our x to false initialy
public static void main(String[] args) {
JFrame f=new JFrame();//creating instance of JFrame
label = new JLabel("0 Sec"); //initialized with a text
label.setBounds(130,200,100, 40);//x axis, y axis, width, height
action=new JButton("Play");//initialized with a text
action.setBounds(130,100,100, 40);//x axis, y axis, width, height
f.add(action);//adding button in JFrame
f.add(label);
action.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
if(x){
x = false; //Update x here
action.setText("Play");
action.revalidate();
}else{
x = true; //Update x here also
action.setText("Pause");
action.revalidate();
if(x){ //Using x here to determind whether we should start our child thread or not.
(new Thread(new Child())).start();
}
}
}
});
f.setSize(500, 700);//500 width and 700 height
f.setLayout(null);//using no layout managers
f.setVisible(true);//making the frame visible
}
}
class Child implements Runnable{
#Override
public void run() {
while (x) {
//You can put your long task here.
i++;
label.setText(i+" Secs");
label.revalidate();
try {
sleep(1000); //Sleeping time for our baby thread ..lol
} catch (InterruptedException ex) {
Logger.getLogger("No Foo");
}
}
}
}
This is my first time trying to use another thread in java, could someone tell me how to make it work please? I've read others topics about it, but I didn't find a solution.
I'd like to draw a gif in another thread (drawn at at a random position and during the duration of its animation).
The problem is that drawImage() in the second thread just doesn't do anything. My counter works well (it prints 1.. 2.. 3 ...), but no image is drawn (or I can't see it).
The condition is false at the begining, then it is true at one moment (to create only one new thread and no more), then it is false again.
if (condition) {
(new ThreadGif(this,g)).start();
}
However when I remove the condition in paintComponent(), it draws something which means that drawImage() works. So when it creates lots of new threads, every image of the gif is drawn at a random location and it starts the gif again and again (and the counter(s) still work well).
This could be fine, but I don't think creating thousands of new thread is the answer : I just need one. And also, I need just one random position for each gif, not one different for each image of the gif.
I hope I've been clear enough. Please help me understand how to make it work :) Thank you very much.
Here are simplified versions of my two classes :
ThreadGif.java :
public class ThreadGif extends Thread {
Screen screen;
Graphics g;
boolean running = true;
public ThreadGif(Screen screen, Graphics g) {
this.g = g;
this.screen = screen;
}
public void run() {
int aleaX = new Random().nextInt(300)/100;
int aleaY = new Random().nextInt(300)/100;
int compt = 1;
while (running) {
g.drawImage(new ImageIcon("res/feu.gif").getImage(), screen.tailleCase*aleaX, screen.tailleCase*aleaY, screen.tailleCase*2, screen.tailleCase*2, screen);
System.out.println("thread " + compt);
compt++;
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
}
}
Screen.java :
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
public Screen(Frame frame) {
this.frame = frame;
thread.start();
}
public void paintComponent(Graphics g) {
g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
if (condition) {
(new ThreadGif(this,g)).start();
}
}
public void run() {
while (running) {
repaint();
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
System.exit(0);
}
}
Pretty simple: you mixed two approaches up.
The paintComponent-method launches a new ThreadGif every time it's called and ThreadGif itself paints within it's thread until it's terminated, but without refreshing the screen.
These two approaches combined might either result in strange behaviour, e.g. two images painted where pieces overlay each other, or the new ThreadGif simply renders the new image every-time you repaint the screen.
Solution: Start by assigning each class specific tasks, without mixing anything up, or splitting tasks between two classes. E.g.:
ThreadGif doesn't paint anything itself, but repaints the Screen. The Screen can request the image that should be displayed from ThreadGif.
Make ThreadGif an own Component that handles it's own rendering and ommit the Screen-class from painting anything.
i'm using a label for a little sprite for some testing that i'm doing, but i want to move the sprite 10 pixels per keypress. Now, i can do that, but now i'm trying to make it move the 10 pixels smoothly, so i tried the next code:
for(int i = 0; i < 100; i++){
x++;
container.setLocation(x, y);
System.out.println(x);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Now the problem is that, the sprite only moves when the for cycle ends, but the console shows the X value changing for each iteration. Any thoughts/help?
Thanks!
I suggest you to take a look at how to animate a JComponent using Swing Timer class, instead of for loop. You can find various tutorials about how to use Swing Timer. Here, to briefly explain, you are blocking EDT(Event Dispatch Thread) which operates the graphical side of the Java. Whenever you want to make a constant and smooth flow in your animations, make sure that you never block the EDT.
EDIT: Here is the demonstration of the usage of Swing Timer Class:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class AnimationTrial extends JFrame {
private final int DELAY = 10;
private Timer timer;
private int x, y;
private JLabel label;
public static void main(String[] args) {
EventQueue.invokeLater( new Runnable () {
#Override
public void run() {
new AnimationTrial();
}
});
}
public AnimationTrial()
{
setSize(500, 500);
x = 50;
y = 50;
label = new JLabel("They see me movin' they hatin'!");
timer = new Timer( DELAY, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0) {
x++;
label.setLocation(x, y);
}
});
timer.start();
getContentPane().add(label);
pack();
setVisible (true);
}
}
If you dont create new Thread, the user interface runs on the same thread as its method.
Therefore your for-cycle is fired after some action and thread cant do anything else until it ends.
Solution : Create your own class, pass the JLabel or the whole form as parameter in constructor, implement threading and run it as new thread.
I'd suggest you give a look to the Timing Framework, if you want to do something close to an animation in Swing. It could help you, depending on your general need.
If you want other sprites to move in sync with your sprite you can create a TimerTask and use scheduleAtFixedRate(). Your TimerTask would then be responsible for moving all sprites and redrawing everything that was part of the moving like the JPanel in the background and the sprites.
To make your code snippet work you would have to add redrawing of the Background and the sprite after setting the location but I would advise against that approach as it can easily lead to badly designed code where you create one God Class that does everything.
The TimerTask approach should also be more precise if the calculations need a bit time as it tries to have the same time between 2 calls where the approach with the sleeping thread can easily lead to different delays if the calculations are finished earlier or later.
Before you read, this will be informative: Java JFrame won't show up after using .setVisible(true) after being invisible
Hello I am working on a library API that let's you capture an area of the screen, and it returns you a class that contains the ByteArrayInputStream and utility methods like createBufferedImage, createFile, etc.
Basically you create a Bootstrap instance, and pass the capturer type you want as a dependency (ScreenshotCapturer or GifCapturer):
Bootstrap b = new Bootstrap(new ScreenshotCapturer());
And the beginCapture method receives an object that implements ScreenCaptureCallback which is the callback event that the captured result will be passed to.
This is a short background.
Now when you use the beginCapture method, basically what it does is creates new instance of SelectionCamera, this is basically the component that paints the selection area you're selecting when dragging the mouse, and updates the listeners.
once created instance, it calls super.setVisible(true);
After that method gets called, the frame will show up, and also show the old painted screen for like 600-500miliseconds, I am not exactly sure, but it disappears so quickly.
Take a look at this live example:
Note use the video option, otherwise you will not see what I'm seeing as gif is too slow to show it!
http://gyazo.com/d2f0432ada37842966b42dfd87be4240
You can see after I click Screenshot again, it shows the old selected area and disappears quickly. (by the way the frame you see in the gif is not part of the app, just dummy hello world example usage).
The process of image capture.
Step 1
beginCapture gets called:
public void beginCapture(final ScreenCaptureCallback c) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
capturer.setCallback(c);
capturer.beginSelection();
}
});
}
Step 2
beginSelection gets called in the Capturer class (ScreenshotCapturer extends Capturer (abstract)
#Override
public void beginSelection() {
super.init();
this.setHotkeys();
super.getCamera().startSelection();
}
Step 3
CaptureCamera#startSelection() gets called
public void startSelection() {
super.getContentPane().removeAll();
super.getContentPane().repaint();
super.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
this.selector = new SelectionCamera();
this.selectionMosueAdapter.updateCamera(this.selector);
this.selectionMouseMotion.updateCamera(this.selector);
super.add(this.selector);
super.setVisible(true);
super.repaint();
super.getContentPane().repaint();
}
Step 4
The user selects an area, and both mouse listener and mouse motion listens to it(Take a look at mouse motion):
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
Point startPoint = this.selector.getStartPoint();
int x = Math.min(startPoint.x, dragPoint.x);
int y = Math.min(startPoint.y, dragPoint.y);
int width = Math.max(startPoint.x - dragPoint.x, dragPoint.x - startPoint.x);
int height = Math.max(startPoint.y - dragPoint.y, dragPoint.y - startPoint.y);
this.selector.setCameraDimension(width, height);
this.selector.setCoordinates(x, y);
this.camera.repaint(); // important
}
by the way this.selector is SelectorCamera which is the component that paints the selection area.
Step 5
CaptureCamera#endSelection() gets called, this method gets the x,y, width, height from the selection camera and passes it to the capturer class which uses Robot to get screenshot with that rectangle, and before that it removes ALL components from the content pane, and repaints everything and then sets visibility to false.
public void endSelection() {
super.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
int x = this.selector.getCameraX();
int y = this.selector.getCameraY();
int w = this.selector.getCameraWidth();
int h = this.selector.getCameraHeight();
super.getContentPane().removeAll();
super.getContentPane().repaint();
//super.repaint();
super.setVisible(false);
this.c.startCapturing(x, y, w, h);
}
Basically this is the last step, rest steps are unnecessary for the debugging as it only sends back the callback.
I really tried my best explaining the process of my application, I've tried figuring it out for 5 and half hours now, and no luck at all. Tried different ways, by creating new SelectionCamera object as you see, doesn't work.
Why is it doing this? Is it something to do with the swing core?
SelectionCamera code: https://github.com/BenBeri/WiseCapturer/blob/master/src/il/ben/wise/SelectionCamera.java
Thanks in advance.
Based on this example...
try {
final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
System.out.println("...");
JFrame frame = new JFrame();
frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
});
}
});
System.out.println("Hello");
} catch (AWTException exp) {
exp.printStackTrace();
}
I won't focus on the initialise stage of the first round, I will focus on the initialisation of the second round as this is where the problem is...
b.beginCapture call's this.capturer.beginSelection();, which calls super.getCamera().startSelection(); which calls setVisible(true) (CaptureCamera been a JFrame).
This will immediately show what ever was previously displayed on the CaptureCamera. It's important to note here, that no new instances of objects were created through the process...
Now, I made a lot of changes to the base testing this, but it appears that the problem is with the restoration of the frame when it's made visible for the second time. This seems to be an issue with the transparency support of the Window as it seems to restore the last "known" state instead of repainting the window immediately...
Now, I tried clearing the selector before making the CaptureCamera invisible to no eval, as the window seems to be made invisible before the selector is painted.
The final solution I came up with was to call dispose on the CaptureCamera, which releases it's native peer and therefore destroys the old graphics context, forcing the frame to rebuild itself when it is made visible again.
"A" problem with this could be the fact that when all the windows are disposed (and the only running threads are daemon threads), the JVM will exit...
This was an issue during my testing as I was using a javax.swing.Timer to put a delay between the first and second capture process so I could see where the problem was occurring and this caused my JVM to exit (as the timer uses a daemon thread and I had no other windows open).
I got around this by creating a tiny (1x1) transparent window in the Capturer class, keep this in mind if the JVM exists gracefully for no reason ;)
Side Notes...
Now, there is an issue with SelectionCamera (which extends JPanel), it is opaque, but is using a transparent background, this is incredibly dangerous as Swing only knows how to deal with opaque or fully transparent components.
public SelectionCamera() {
super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
Should be updated to something like...
public SelectionCamera() {
setOpaque(false);
//super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
I'm also confused over the use of super.xxx, the only reason you would do this is if you had overrriden those methods and didn't want to call them at this time...In my testing, I removed all the calls to super where a method wasn't overridden in the current class (and I wasn't already in the overriden method)
Also, the paintComponent method should be calling super.paintComponent
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0, 0.5f));
g.fillRect(this.x, this.y, this.width, this.height);
}
Make Frame left to be -10,000 then set visible true, add a timer 2 seconds (try lower to 25-100 milliseconds, just to give it sligth pause to invalidate content) , on timer :left to 0 . I think it works due to caching & double buffereing. Frame shows what it had in buffer, buffer points to old image due to caching/ lazy repaint.
Alternative :
Maybe a repaint or invalidate before your show would work too, and don't need to do the left -10,000. I dont work much with ui-swing, just a but years back and remember some strange things like this.
I already have a post related to the multithreading issue but I have some new questions+code. I have a multiball game project and it requires me to parse an XML file to obtain information about the ball(like size, speed, initial position etc). Now, I wanted to create a different thread to parse the XML file, but I cannot figure out a way to do it. Here is my code:
main() starts here:
public class BounceBallApp extends JFrame{
public BounceBallApp()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("BounceBallApp");
setSize(300,300);
setVisible(true);
add(new BallWorld());
validate();
}
public static void main(String[] args)
{
/*Create main GUI in the Event Dispatch Thread*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new BounceBallApp(); //main frame
}
});
}
}
Within the constructor for BallWorld(), I have an inner class BallContainer(), which contains a Start button:
jbtStart.addActionListener(new ActionListener()
{public void actionPerformed(ActionEvent e)
{
//Populate the ballList arraylist
if(filePathField.getText().equals(" "))
{
JOptionPane.showMessageDialog(null, "Please input the XML file","Information", JOptionPane.INFORMATION_MESSAGE);
}
else
{
XMLFilePath = filePathField.getText();
ballList = new BallList(XMLFilePath);//I want to put this in a thread
JOptionPane.showMessageDialog(null,"Game started!","Bouncing Balls",JOptionPane.INFORMATION_MESSAGE);
for(Ball ball:ballList.ballsArrayList)
{
timer.setDelay(1000/ball.getSpeed()); //change the delay of the timer to the ball's speed
timer.start(); //start the timer
bTimer = true; //timer is now on
}
}
}
});
}
Now the problem is that if I put the parsing process in another thread, then I have to wait for the ballsArrayList to fill before I can continue with the application. I was thinking of using invokeAndWait() but I read that that method cannot be called from the Event Dispatch Thread. So, How can I achieve this? Or is it even worthwhile?
Also, I wanted to move the calculation for moving the ball (calculating the x,y coords) to a thread, but again, I don't know how to implement it.
for(Ball ball:ballList.ballsArrayList)
{
ball.draw(g);
ball.move(ballContainerWidth,ballContainerHeight,buttonPanel.getHeight());
}
public void move(int ballContainerWidth,int ballContainerHeight,int buttonPanelHeight)
{
if((this.getX()+this.getsize()+this.getDx()) > ballContainerWidth)
{
this.setDx(-Math.abs(this.getDx()));
}
//the height/depth to which the balls can bounce is the (main ball container height) minus (button panel height)
if((this.getY()+this.getsize()+this.getDy()) > ballContainerHeight-buttonPanelHeight)
{
this.setDy(-Math.abs(this.getDy()));
}
if((this.getX()-this.getsize()) < 0 )
{
this.setDx(Math.abs(this.getDx()));
}
if((this.getY()-this.getsize()) < 0 )
{
this.setDy(Math.abs(this.getDy()));
}
int newX = (int)Math.round((this.getX()+this.getDx()));
int newY = (int)Math.round((this.getY()+this.getDy()));
this.setX(newX);
this.setY(newY);
}
Sorry for the long post, but multithreading is all new to me. I am a bit confused about it.
Initial loading of the files
I personally would opt for one of the following approaches
Increase the start-up time of your program by parsing all the files during start-up. For a few XML files this overhead might be very small. If it takes too long, you can consider showing a splash screen
Load the XML files when the start button is pressed, but show a progress bar until the loading is done. Start the game afterwards. A SwingWorker can help you with this. Examples can be found in the Swing documentation or here on SO.
Updating of the ball position
If the calculation is as easy as what is shown here, I would simply use a javax.swing.Timer to update the position on regular time intervals, and do the calculation on the Event Dispatch Thread.
If you want to do the calculation on a background thread just for the exercise, I would still opt for a calculation of the position on a background thread. The calculation should be using local variables which are only know to that background thread. Once the new position is calculated, update the position of the ball on the Event Dispatch Thread using SwingUtilities#invokeLater. This allows you to access the position during the paint operation without having to worry about threading issues. Probably easier then messing around with locks.