The simple piece of midlet code (class Moo) below (after the excerpts) deadlocks (At least I assume it deadlocks after reading this post on threads here).
I have reproduced the relevant excerpts from the post :
String url = ...
Connection conn = null;
try {
conn = Connector.open( url );
// do something here
}
catch( IOException e ){
// error
}
The root of the problem is the blocking nature of the open() call. On some platforms, the system does the actual connection under the covers, on the equivalent of a separate thread. The calling thread blocks until the connection thread makes the connection. At the same time, the security subsystem may require the user to confirm the connection, and the connection thread blocks until the event thread gets confirmation from the user. Deadlock occurs because the event thread is already waiting for the connection thread.
public class Moo extends MIDlet {
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// TODO Auto-generated method stub
}
protected void pauseApp() {
}
protected void startApp() throws MIDletStateChangeException {
Display display = Display.getDisplay(this);
MyCanvas myCanvas = new MyCanvas();
display.setCurrent(myCanvas);
myCanvas.repaint();
}
class MyCanvas extends Canvas {
protected void paint(Graphics graphics) {
try {
Image bgImage = Image.createImage(getWidth(), getHeight());
HttpConnection httpConnection = (HttpConnection) Connector
.open("http://stackoverflow.com/content/img/so/logo.png");
Image image = Image.createImage(httpConnection
.openInputStream());
bgImage.getGraphics().drawImage(image, 0, 0, 0);
httpConnection.close();
graphics.drawImage(bgImage, 0, 0, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Can someone please tell me how the system thread invocation is done here (event and notification threads) and the sequence of events leading to the deadlock. I am not clear as to what are the thread involved here that lead to deadlock.
Is there any documentation on j2me thread model?
Where can I get the sources for j2me system classes (I want to check out the implementation of Connection classes)?
EDIT : In the above code I get the logic. But the below code should at least work right? This one also deadlocks where I am doing the network connection in a separate thread.
public class Foo extends MIDlet {
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// TODO Auto-generated method stub
}
protected void pauseApp() {
// TODO Auto-generated method stub
}
protected void startApp() throws MIDletStateChangeException {
Display display = Display.getDisplay(this);
MyCanvas myCanvas = new MyCanvas();
display.setCurrent(myCanvas);
myCanvas.repaint();
}
class MyCanvas extends Canvas {
protected void paint(Graphics graphics) {
try {
Image bgImage = Image.createImage(getWidth(), getHeight());
FetchImage fetchImage = new FetchImage();
Thread thread = new Thread(fetchImage);
thread.start();
thread.join();
bgImage.getGraphics().drawImage(fetchImage.image, 0, 0, 0);
graphics.drawImage(bgImage, 0, 0, 0);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class FetchImage implements Runnable {
public Image image;
public void run() {
HttpConnection httpConnection;
try {
httpConnection = (HttpConnection) Connector
.open("http://10.4.71.200/stage/images/front/car.png");
image = Image.createImage(httpConnection.openInputStream());
httpConnection.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Where can I get the sources for
j2me system classes (I want to check
out the implementation of Connection
classes)?
You cant. Its actually vendor dependent. The way in which Nokia handles this situation may be different from Motorola.
The lesson you have to learn is that don't do expensive computation in system callbacks as that might make the system unresponsive. So put the time consuming operation in a sepearate thread and always return from call backs as early as possible.
Even though you have created a separate thread in your second example, you wait for its completion in paint() and it eventually makes a result of no threading at all!
One thing you can do is
class MyCanvas extends Canvas {
Image image;
boolean imageFetchFailed;
protected void paint(Graphics g) {
if (image == null) {
fetchImage();
g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP)
} else if (imageFetchFailed) {
g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP)
} else {
g.drawImage(image, 0, 0, 0);
}
}
private void fetchImage() {
new Thread(new Runnable() {
public void run() {
HttpConnection httpConnection = null;
try {
httpConnection = (HttpConnection) Connector
.open("http://10.4.71.200/stage/images/front/car.png");
image = Image.createImage(httpConnection.openInputStream());
} catch (IOException e) {
e.printStackTrace();
imageFetchFailed = true;
}
if (httpConnection != null) {
try {
httpConnection.close();
} catch (IOException ignored) {
}
}
// Following will trigger a paint call
// and this time image wont be null and will get painted on screen
repaint();
}
}).start();
}
}
Canvas.paint() is a event delivery method, which means it is called by a system event thread.
Assume on a system, both Canvas.paint() calling and user confirmation event handling are implemented to be done by a UI event thread (UT).
Now, when UT is blocked inside Canvas.paint() by Connector.open(), UT sure not able to handle the next coming event, which in this case is the user confirmation event triggered by Connector.open(). It is not possible for UT to process another event when it's blocked inside your application code.
That's why the deadlock occur here, the connection thread is waiting for something will never happen, and blocks UT forever.
In general, you should not expect how will system event thread be implemented, and try to return from event handling method as quickly as possible. Otherwise, you might receive lower performance or deadlock like this.
Well, the basic problem is that some Java VM implementations use the same Java thread to do everything.
One of the first thing you need to figure out about the threading model of your VM is who developed it.
There is a list of J2ME licensees here: http://java.sun.com/javame/licensees/index.jsp
From that information, try to figure out how many native threads your VM is using.
The 2 usual models are either run all bytecode interpretation into one native thread or run each java thread into its own native thread.
The next step is to gather information about how asynchronous the underlying operating system APIs are. When developing the VM, the licensees would have had to write native code to port the VM to the operating system. Any Inter-Process Communication or use of slow transmission medium (from flash card to GPS signal) might be implemented using a separate native thread that could allow the bytecode interpreter thread to keep running while the system waits for some data.
The next step is to realize how badly implemented the VM is. Typically, your problem happens when the VM uses only one internal java thread for all callbacks methods in the MIDP spec. So, you don't get a chance to react to keypad events until after your connection is opened if you try to open it in the wrong java thread.
Worse, you can actually prevent the screen from being refreshed because Canvas.paint() would be called in the same java thread as javax.microedition.media.PlayerListener.playerUpdate() for example.
From the VM implementation perspective, the golden rule is that any callback that you don't control (because it can end up in "user" code, like a listener) cannot be called from the same java thread you use unblock standard API calls. A lot of VM out there simply break that rule, hence the Sun recommendation that JavaME developers work around it.
Some good ideas, but it seems that there is a race condition in Manoj's example.
Multiple paint calls is possible while an image is downloading, causing multiple threads to be created all downloading the same image (One example of extra paint calls is when the HTTP connection prompt pops up).
Since all paint calls are made on the same thread, we can avoid synchronization by testing and setting a flag inside the paint call. Below is an attempt at an improved version:
class MyCanvas extends Canvas {
Image image;
boolean imageDownloadStarted;
boolean imageFetchFailed;
protected void paint(Graphics g) {
g.fillRect(0, 0, g.getClipWidth(), g.getClipHeight());
if (image == null) {
if (imageDownloadStarted)
return;
imageDownloadStarted = true;
fetchImage();
g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP);
} else if (imageFetchFailed) {
g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP);
} else {
g.drawImage(image, 0, 0, 0);
}
}
private void fetchImage() {
new Thread(new Runnable() {
public void run() {
try {
final HttpConnection httpConnection = (HttpConnection) Connector.open("http://stackoverflow.com/content/img/so/logo.png");
try {
final InputStream stream = httpConnection.openInputStream();
try {
image = Image.createImage(stream);
} finally {
stream.close();
}
} finally {
httpConnection.close();
}
} catch (IOException e) {
e.printStackTrace();
imageFetchFailed = true;
}
repaint();
}
}).start();
}
}
Note the use of the final key word to avoid a null test and the explicit closing of the stream returned by openInputStream.
Related
I created a popup window to show progress of something, and it is working fine with my downloader, everything is being updated.
private void downloadFile(String link, String directory, String name) throws IOException {
task = new Downloader(link, directory, name);
task.start();
}
and in the Downloader class:
public void run() {
try {
while ((length = is.read(b)) != -1) {
downloaded += length;
bout.write(b, 0, length);
int percent = (int) ((downloaded * 100) / fileLength);
window.modify1("Downloading " + name + ".jar");
window.modify2((int) downloaded, "Progress: " + percent + "% [" + String.valueOf(downloaded).subSequence(0, String.valueOf(downloaded).length() - 1) + "kb/" + String.valueOf(fileLength).subSequence(0, String.valueOf(fileLength).length() - 1) + "kb]");
}
is.close();
bout.close();
window.exit();
Creator c = new Creator(directory, name);
c.create();
this.join();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
However, when I attempt to do almost the same in another thread, it does not work. Nothing in the popup window is being shown until the thread finishes.
LauncherThread t = new LauncherThread();
t.start();
try {
t.join();
} catch (InterruptedException e2) {
e2.printStackTrace();
}
and in the LauncherThread class:
public void run() {
window.modify1("Fetching data...");
window.modify2(0, "Progress: 0% [0/10]");
Main.trust();
window.modify2(1, "Progress: 10% [1/10]");
Main.bukkitVersions = Finder.findBukkitVersions();
window.modify2(2, "Progress: 20% [2/10]");
Main.spigotVersions = Finder.findSpigotVersions();
window.modify2(3, "Progress: 30% [3/10]");
Main.vanillaVersion = Finder.findVanillaVersion();
window.modify2(4, "Progress: 40% [4/10]");
Main.bukkitLinks = new String[3];
Main.bukkitLinks[0] = Finder.findDownloadLink("bukkit", "rb");
window.modify2(5, "Progress: 50% [5/10]");
Main.bukkitLinks[1] = Finder.findDownloadLink("bukkit", "beta");
window.modify2(6, "Progress: 60% [6/10]");
Main.bukkitLinks[2] = Finder.findDownloadLink("bukkit", "dev");
window.modify2(7, "Progress: 70% [7/10]");
Main.spigotLinks = new String[2];
Main.spigotLinks[0] = Finder.findDownloadLink("spigot", "lastStable");
window.modify2(8, "Progress: 80% [8/10]");
Main.spigotLinks[1] = Finder.findDownloadLink("spigot", "lastBuild");
window.modify2(9, "Progress: 90% [9/10]");
Main.vanillaLink = Finder.findDownloadLink("vanilla", null);
window.modify2(10, "Progress: 100% [10/10]");
window.exit();
}
I'm still quite new to Java so I apologize for my ignorance.
Edit: I encased the t.start() method inside a SwingUtilities.invokeLater(), and it works now. But the new problem is that the main class is no longer waiting for the LauncherThread to finish.
I went with the easy (and possibly bad) way. the initialize method is called in the LauncherThread after it finishes.
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new Main();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Main() {
launch();
}
/**
* Run launcher.
*/
private void launch() {
LauncherThread t = new LauncherThread(this);
t.start();
}
I pass the Main instance to the LauncherThread class through its constructor, so I can call the initialize method from there without having to use static access.
main.initialize();
You are in the process of running into serious problems and I'm afraid your current solution doesn't solve the problem but just hides it.
The root problem is that Swing (as most comparable libs) isn't thread safe. So there must only one thread that accesses Swing components. That thread isn't arbitrary, but a special thread named EDT (Event Dispatch Thread).
The consequences of that are:
The initialization of your UI must be wrapped in a call to SwingUtilities.invokeLater or SwingUtilities.invokeAndWait
If you do work in a separate thread that code must not touch your Progressbar directly. Instead it must dispatch updates to the progress bar through the two methods mentioned above.
If you don't do this right now and your code seams to work, it does not mean it will work tomorrow, or on a different machine, or when the weather changes or the government in china changes.
If you want, one thread to wait on another you should look into stuff like CountDownLatch just make sure you don't block the EDT, because when it is blocked nothing will get painted in the UI, so the application seems to be dead for the user. Again executing some code through invokeLater might solve that problem.
When i have to update Swing components from another Thread, then i'm doing it in
SwingUtilites.invokeLater(new Runnable() {
//set new values
});
from Javadoc ot method:
Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This will happen after all pending AWT events have been processed. This method should be used when an application thread needs to update the GUI.
One more thing,.
You might consider doing your application via SwingWorker, those object are friendly with EDT. But remember, that they are executed in limited ThreadPool. I tried once to launch 50+ swing workers to update components of panel, however it worked only for first 10 of them. Probably could be changed to have them all running, but I've changed my approach.
I use Clips in a game. The clips play fine, but after some "shots", the following problem occurs
Exception in thread "PulseAudio Eventloop Thread" java.lang.IllegalStateException: drain failed
at org.classpath.icedtea.pulseaudio.EventLoop.native_iterate(Native Method)
at org.classpath.icedtea.pulseaudio.EventLoop.run(EventLoop.java:133)
at java.lang.Thread.run(Thread.java:724)
My code:
public static Clip[] sounds;
...
sounds = new Clip[3];
sounds[0] = getClip("gun.wav");
sounds[1] = getClip("click.wav");
sounds[2] = getClip("over.wav");
...
private void playSound(Clip clp) {
final Clip clip = clp;
Runnable soundPlayer = new Runnable() {
#Override
public void run() {
try {
if(clip.isActive() || clip.isRunning()) clip.stop();
clip.setMicrosecondPosition(0);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(soundPlayer).start();
}
public void shoot() { //runs when space is clicked
if(canShoot) playSound(sounds[0]);
}
So I had a similar problem on OS X, where the clip was sometimes not playing if you tried to stop it and restart it from the beginning. I fixed it by calling flush() right after stop():
if(clip.isActive() || clip.isRunning()) {
clip.stop();
clip.flush();
}
You shouldn't need to spawn a background thread to interact with the Clip. The most commonly used methods of Clip, like "start" and "stop" operate asynchronously, meaning they do not block, so it should be okay to call them from the GUI/AWT/Swing thread.
This page has some reasonable examples:
http://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
I see you are using IcedTea and PulseAudio. You may experience different behavior when using javax.sound in this JVM, as opposed to the Oracle JVM, since the implementations of javax.sound are significantly different between these two products.
Is there any way/tool to control the threads running within a process on android platform, such as let some threads delay for a random time.
Background: I am a QA engineer. I'd like to see if some threads are forced to run slowly, what will happen to the whole app? I'd like to see other kind of errors rather than ANR. For multiple threading, if programmer doesn't use or use bad strategies to sync threads, some bugs might happen. So I want to do this kind of testing.
You only have to take care of the UIThread, well actually Android will take care of it for you. Taking that into account, try to don't do any intensive operation in this thread as you won't have full control of it (see activity lifecycle)
As long as the UIthread is fine, you won't notice a slow responding app, for the rest of threads I suggest you taking a look in different classes that will ease the task of communicating back with this UIThread;
Asynctask & Handlers, there are more options, but these two are the most important (imo)
The rest of threads, you can control them as you would in Java, even sleeping them if needed.
Let's see an example:
public class MapView extends SurfaceView implements Runnable{
Thread t = null;
SurfaceHolder holder;
boolean draw = false;
#Override
public void run() {
while (draw) {
if (!holder.getSurface().isValid())
continue;
Canvas c = holder.lockCanvas();
//Draw something
holder.unlockCanvasAndPost(c);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void pause() {
draw = false;
try {
t.join(); // this will cause the main thread to wait for this one to
//finish and then it can safely access its data.
} catch (InterruptedException e) {
e.printStackTrace();
}
t = null;
}
void resume() {
draw = true;
t = new Thread(this);
t.start(); // This will look for our run method (this)
}
}
In this example, a normal Thread is used to control how/when and with what delay we draw. The resume and pause methods let us control that thread so we can stop drawing if the activity using it is in background and restart it when it comes back (Overriding onPause and onResume)
I have what I thought was a simple problem, which I have yet to find a good solution to: I would like to be able to pause and unpause the activity taking place in a thread, by hitting a button on a Swing interface panel.
Specifically, I would like to use one thread to take in audio frames in real time; a second thread to perform magic processing on those frames; and a third thread to serialize the results and send over a socket somewhere else. The kicker is that depending on the brand of magic we employ, the processing in the second thread might take longer per frame to perform than the actual data collection, and the data might pile up after a while.
As a very crude prototype workaround we thought we'd add a GUI with a button to turn the audio collection process on and off and a status bar (to be implemented later) so that a user could sort of keep an eye on how full the buffer (a linked blocking queue) happened to be.
This is harder than I anticipated. I've stripped the problem down to a toy version: A linked blocking queue that can store 50 Integers, a GUI, two threads (adding to and removing from the queue at different rates) and a Token object wrapped around a boolean. It looks like this, and it sorta works:
Test.java
public class Test {
public static void main(String[] args) throws IOException {
Token t1 = new Token();
Token t2 = new Token();
LinkedBlockingQueue<Integer> lbq = new LinkedBlockingQueue<Integer>(50);
startFill sf = new startFill(t1, lbq);
startEmpty se = new startEmpty(t2, lbq);
TestUI testUI = new TestUI(t1, t2, lbq);
testUI.setVisible(true);
sf.start();
se.start();
}
}
TestUI.java
public class TestUI extends JFrame implements ActionListener {
private JToggleButton fillStatus, emptyStatus;
public boolean filling, emptying;
public Token t1, t2;
public LinkedBlockingQueue<Integer> lbq;
public TestUI(Token t1, Token t2, LinkedBlockingQueue<Integer> lbq) {
this.t1 = t1;
this.t2 = t2;
this.lbq = lbq;
initUI();
}
public synchronized void initUI() {
JPanel panel = new JPanel();
panel.setLayout(null);
filling = false;
fillStatus = new JToggleButton("Not Filling");
fillStatus.setBounds(20, 20, 150, 25);
fillStatus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (filling == false) {
fillStatus.setText("Filling");
} else {
fillStatus.setText("Not Filling");
}
filling = !filling;
t1.flip();
System.out.println("fill button press");
}
});
// Similar code for actionListener on Empty button, omitted
panel.add(fillStatus);
panel.add(emptyStatus);
add(panel);
setTitle("Test interface");
setSize(420, 300);
setLocationByPlatform(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
}
}
startFill.java
public class startFill extends Thread {
public Token token;
public LinkedBlockingQueue<Integer> lbq;
public startFill(Token token, LinkedBlockingQueue<Integer> lbq) {
this.token = token;
this.lbq = lbq;
}
public void run() {
int count = 0;
while (true) {
while (!token.running()) {
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (token.running()) {
try {
lbq.put(count);
System.out.println("queue size = " + lbq.size());
count++;
sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
}
There is also a startEmpty.java that works about the same way, and a Token.java that's a wrapper for a boolean state variable, omitted for merciful brevity.
So that works, but at the expense of polling in the while (!token.running()) loop.
I tried using Locks and Conditions, but failed, always getting IllegalMonitorStateExceptions.
And I looked at this similar question and managed to get that working, but at the expense of using the yield() method which apparently differs significantly between Java 5 and Java 6, and seems to be highly discouraged.
So my question: Is there some correct, or significantly better way to do what I am trying to do? It seems like there should be a way to make this happen without the polling and with reliable methods.
Update: I'm not sure I can get around the issue of controlling the audio capture loop in some way for the application. Whether it is a human pressing a button, or internal logic making decisions based on some other factors, we really need to be able to shut the darn thing down and bring it back to life on command.
Instead of handling the synchronisation between your 3 worker processes by hand via a GUI, you could also setup a factory lineup between the workers:
add 2 queues between your workers
block your threads on queue-state conditions;
readers (consumers) block on empty queue
writers (producers) block when the queue is full (say 2n messages where n is the number of consumers for that queue.)
wait() on a queue to block your thread and notifyAll() on that queue after adding or removing a message from a queue.
A setup like this automatically slows down producers running faster than their consumers.
Why dont you implement ArrayBlockingQueue.
Its Better use ArrayBlockingQueue class which is present in java.util.concurrent package, which is Thread Safe.
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(100);
Here is one way to do what I was trying to do: Properly use wait() and notify(), synchronized on the Token objects, like such:
startFill.java run() method
public synchronized void run() {
int count = 0;
try {
// token initializes false
// wait until notification on button press
synchronized (token) {
token.wait();
}
// outer loop
while (true) {
// inner loop runs as long as token value is true
// will change to false on button press
while (token.running()) {
lbq.put(count);
System.out.println("queue size = " + lbq.size());
count++;
sleep(100);
}
// wait until notification on button press, again
synchronized (token) {
token.wait();
}
}
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}
TestUI.java ActionListener:
fillStatus.addActionListener(new ActionListener() {
// t1 was initialized false
public void actionPerformed(ActionEvent event) {
if (filling == false) {
fillStatus.setText("Filling");
// if false, change t1 status to true
t1.flip();
// and send the notification to the startFill thread that it has changed
synchronized (t1) {
t1.notify();
}
} else {
fillStatus.setText("Not Filling");
// if true, change t1 status to false
t1.flip();
// no notification required due to polling nature of startFill's active thread
}
filling = !filling;
System.out.println("fill button press");
}
});
This works rather nicely, without polling while the thread is turned off.
My initial attempts at this failed due to bad syntax-- I neglected the synchronized (token) {...} context block around the wait() and notify() statements.
I've already browsed many threads on this topic, but nothing seems to fit my specific situation.
I have a swing application which analyzes QR-Codes, extracts the found loginNames and makes DB calls to fetch data for that user. To make sure the capture of the QR-Codes can be canceled and my application is still accessible while capturing, I used a SwingWorker for this purpose. Everything works fine so far, I've included a PropertyChangeListener so the application knows when my SwingWorker successfully read a code. But since I don't want to have the PropertyChangeListener as a nested class within my mainClass (to keep it well structured), I've created a new class for it outside. Now I want to return to my main class from this PropertyChangeListener class to switch to the appropriate panel which displays the fetched data. I have different codes which can be read, so depending on the code I have different panels to switch to (so I can't do a static switch to the same panel over and over again). So how can I delegate the PropertyChangeListener to give the control back to my EDT ?
I've tried using wait() and notify() to let my EDT know the SwingWorker finished. But obviously wait() blocks my EDT and the use of SwingWorker is pointless.
I hope I could explain my problem in enough detail, and some of you have a good idea to deal with this.
For any code snippets please ask, I'll then add the necessary ones. But since my project is a bit more complex I'll just post what is asked for.
Thanks in advance for any help :)
EDIT: Here is a code excerpt to illustrate what my SwingWorker is doing.
SwingWorker class:
public class CodeDetector extends SwingWorker<byte[], String> {
String s; // read String
byte[] completeCode; // byte[] which is returned by doInBackground()
BufferedImage resizedImg;
IplImage img;
JLabel labelForStream;
JLabel result;
FrameGrabber grabber = new VideoInputFrameGrabber(); // using JavaCV.
public CodeDetector(JLabel labelForStream, JLabel result) {
this.labelForStream = labelForStream;
this.resultLabel = result;
}
#Override
protected byte[] doInBackground() throws Exception {
try {
grabber.start(); //
while (true) {
// End if current thread was canceled.
if (Thread.currentThread().isInterrupted()) {
return null;
}
// Grab each image, save it, scan for code and display it.
img = grabber.grab();
resizedImg = // resizing image to fit labelForStream.
// save resizedImg to file
// read barcode from saved file
if (isBadgeCode(tmp) || isDeviceCode(tmp)) {
s = tmp;
} else {
continue;
}
break;
} catch (NotFoundException e) {
// Code could not be encoded yet.
}
...
// end worker after timeout
// show image on window
if (img != null) {
labelForStream.setIcon(new ImageIcon(resizedImg));
}
}
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage() + " - " + e.getStackTrace() + " - " + e.getClass());
}
return s != null ? s.getBytes() : null;
}
#Override
protected void done() {
try {
completeCode = get();
if (completeCode != null) {
String code = new String(completeCode);
if (isOtherCode(code)) {
resultLabel.setText(code);
} else if (isUsernameCode(code)) {
// Cut userName from read code (if previously verified) and set label text.
resultLabel.setText(verify(code, true) ? code.split(":")[0] : null);
}
} else {
resultLabel.setText(null);
}
resultLabel.setVisible(true);
resultLabel.updateUI();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
return;
} catch (Exception e) {
e.printStackTrace();
}
}
As this SwingWorker doesn't have references to any panels, even though the done()-method is done in EDT, I need to somehow notify my mainClass that a code has been read successfully and that it can change the panl now according to the specific code.
Hope this clears things up a bit.
I think that you misunderstood, for what reasons SwingWorker is there, please read SwingWorker
tutorial, where implementations quite guarentee that output from methods:
done()
process()
publish()
setProgress()
should be done on EDT
The simple swing worker answer is to override the done() method. That is executed on the EDT - SwingWorker takes care of that for you.
You can do it yourself, using SwingUtilities.invokeLater.
For the way you pose your question, I suspect you don't have a full grasp of the threading issues and how you switch between threads. So a good review of the tutorial (if you haven't already) may be in order.
This is wrong:
protected byte[] doInBackground() throws Exception {
// ....
if (img != null) {
labelForStream.setIcon(new ImageIcon(resizedImg));
}
// ....
}
As this shows you making critical Swing calls from within the doInBackground method, something that should never be done. Instead consider publishing the Image or the ImageIcon, and setting the JLabel's Icon from the process method override.
As I note in my comment, sometimes its a good idea to use a PropertyChangeListener with your SwingWorker if it decreases code coupling. This is one reason that SwingWorker has its own PropertyChangeSupport and its own state enum.