Java Forum / GUI / October 2005
focus events being received out of sequence
Anup - 23 Sep 2005 18:15 GMT I have a complicated applet where each component is registered with multiple focus listeners. I have several pairs of JTextFields and JButtons - textfield A, button A, textfield B, button B etc. Clicking on a JButton normally causes a JTable to be popped and focus transferred to it. Clicking on the table header or selecting a table row causes the table popup to close and the focus transferred to the textfield associated with the button.
I'm running into a strange case where the focus is initially on textfield A. Clicking on button B causes a temporary loss of focus on textfield A. The trace shows that the focus owner is null and the permanent focus owner is textfield A. The table still gets launched. Selecting a table row causes it to close but no component has focus any more. Focus owner continues to be null, and permanent owner continues to be textfield A.
After working on other windows in the meantime, and returning to this window I find that the focus in now back to textfield B, as it should. The trace shows that B has received permanent focus events. This is followed by receiving permanent focusLost events on A and then permanent focusGained events on A. At the point all in said and done, the focus appears on B.
Does someone know why the focusGained event on B appears before focus events on A? Another strange thing in the trace was that some focus gain/loss method invocations were interrupted, another focus method invoked and the previous method resumed. I could possibly take care of this by using synchronized on all focus gain/loss methods. Also, some methods show only part of execution and not even resumed. This is still a mystery to me. I'll appreciate if someone can shed light on this.
Roedy Green - 24 Sep 2005 02:07 GMT >Does someone know why the focusGained event on B appears before focus >events on A? Another strange thing in the trace was that some focus >gain/loss method invocations were interrupted, another focus method >invoked and the previous method resumed. you do realise you must not invoke swing methods from anything but the Stwing event thread. See http://mindprod.com/jgloss/threads.html
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Anup - 02 Oct 2005 00:23 GMT Roedy, I browsed the link on threads you provided. I liberally sprinkled my code with println of SwingUtilities.isEventDispatchThread() and Thread.currentThread().getName().
I ran the compiled code under JBuilder as well as a signed applet in IE browser. This is what I discovered: 1. In the initial bringup of the form, the main thread returned isEventDispatchThread=false and thread name of main (JB) or class name (IE). ActionPerformed methods returned the same results. FocusGained and Lost returned isEventDispatchThread=true and thread name of AWT-EventQueue-1 or AWT-EventQueue-3. I noticed the threads were interfering with one another. 2. Once the form was up, all of the methods returned isEventDispatchThread=true and the thread name was AWT-EventQueue-1 or AWT-EventQueue-3. I noticed that AWT-EventQueue-1 threads interrupted AWT-EventQueue-3 threads.
The questions I have for you are: 1. On the initial bringup of the form, why do focus gain and lost use awt event queue threads but actionPerformed uses the main thread? 2. On the initial bringup, how can I force everything (main, actionPerformed, focus gain/lost) to run under the same thread? 3. What determines which awt event queue thread focus gain and lost will operate under since they are invoked asynchronously.
Anup
Roedy Green - 02 Oct 2005 04:53 GMT >AWT-EventQueue-1 or AWT-EventQueue-3 What kind of machine are you running? I don't think I have ever seen more than one event queue thread, mind you it is not something I look for regularly. IIRC we debated whether the language spec would permit more than one, and we decided yes.
Have you an "ordinary" machines e.g. Window/Linux desktop to try this on? If it works fine, and you see it has only one event queue, that is pretty good evidence the multiple event queues are the culprit.
I am gaping like a goldfish trying to think what the implications of this are. With multiple event threads how could Swing be thread safe unless you do synchronisation even in your event handling code? How would anyone know to do that?? Help!!! This does not compute.
Is there any possibility you did some odd thing to spawn a new event Thread?
Scan the JDK docs to see if there is anything about multiple event threads.
>The questions I have for you are: >1. On the initial bringup of the form, why do focus gain and lost use >awt event queue threads but actionPerformed uses the main thread? I will first answer how, not why. My answer is only roughly correct. There will be details wrong or simplified, but it will do for the current purpose of giving you a rough internal model for predicting its behaviour..
In beginning, there was just one Thread, Everything was done on it. Java not "know" whether you are going to do a console app, an AWT app or a Swing app. They are all the same when a app starts. You build GUI components in RAM. But they are just ordinary objects. They are not visible. The OS knows nothing about them. All is make-believe so to speak-pure GUI simulation. Further there are no events yet. At some point you call showtime! formally known as JFrame.setVisible( true ). At this point Swing sets up a Thread to service the event queue. The OS creates a peer or peers, and a picture appears on the screen. Mouse events and keyboard events start getting generated and dispatched. All that happens with the SECOND thread. Your mainline code continues to use the main thread. Usually it exits, leaving the JFrame still visible. The SECOND thread keeps servicing events, especially repaint events. So from now on, you would expert anything to run on the SECOND thread.
Now to answer your why question. IIRC, in the olden days of Java 1.0 there was only one thread, and it handled main and events. It did not start handling events UNTIL you called exit in main. Others have told me I just imagined this or read it from some ignoramus. But let's say for argument's sake that it DID work that way. The problem is your GUI would be unresponsive until you called exit. Sometimes your main has useful things to do.
>2. On the initial bringup, how can I force everything (main, >actionPerformed, focus gain/lost) to run under the same thread? There are basically two approaches:
1. spin off event handling code on the background thread.. 2. spin off gui poking code on the event thread.
In any large Swing app you will do both. See http://mindprod.com/jgloss/thread.html SwingWorker for spinning off lengthy event handlers on a private thread.
See http://mindprod.com/jgloss/SwingUtilities.invokeLater to spin of background work onto the Event thread. ALL Swing component poking must be done this way. You must never call a Swing method from anything but the event thread, with a few exceptions listed in my essays on threads.
>3. What determines which awt event queue thread focus gain and lost >will operate under since they are invoked asynchronously. This is where I am utterly baffled at this point. I can't see how Swing could possibly work with two event threads. AWT yes, but not Swing.
I hope someone else will chime in.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Anup - 02 Oct 2005 21:01 GMT > >AWT-EventQueue-1 or AWT-EventQueue-3 > Have you an "ordinary" machines e.g. Window/Linux desktop to try this > on? If it works fine, and you see it has only one event queue, that > is pretty good evidence the multiple event queues are the culprit. Yes, I'm running on windows xp.
> I am gaping like a goldfish trying to think what the implications of > this are. With multiple event threads how could Swing be thread safe [quoted text clipped - 3 lines] > Is there any possibility you did some odd thing to spawn a new event > Thread? Yes, there is! The main thread starts another thread. But this doesn't quite explain it, read further below.
> >2. On the initial bringup, how can I force everything (main, > >actionPerformed, focus gain/lost) to run under the same thread? [quoted text clipped - 3 lines] > 1. spin off event handling code on the background thread.. > 2. spin off gui poking code on the event thread. I tried the invokeLater method in the main thread and also the thread it spawned off. The trace shows that immediately after getting control, the invoked-to methods are running under the same awt event thread.
> >3. What determines which awt event queue thread focus gain and lost > >will operate under since they are invoked asynchronously. > > This is where I am utterly baffled at this point. I can't see how > Swing could possibly work with two event threads. AWT yes, but not > Swing. However, somewhere down the line I do see 2 awt event threads being active with different priority, even after using invokeLater. I have yet to do a detailed analysis of the trace to look for any distinguishing features between the two.
Anup
Roedy Green - 03 Oct 2005 01:50 GMT >Yes, there is! The main thread starts another thread. But this doesn't >quite explain it, read further below. I think I know what you did. This is subtle.
In your main thread, you can get away with poking GUI elements on the main thread before they capable of generating events, namely until you first call pack or setVisible( true ).
From that point on, you must use SwingUtilities.invokeLater or invoke them from the event processing thread.
The question arises, what triggers the creation of the AWT/Swing thread?
I will conjecture it is when you call pack/setVisible from something other than the Swing thread.
I think you did this TWICE, thus triggering two even processing threads! which then proceeded to slash each other to death.
What we need now is a recipe to avoid this disaster.
I suspect there are two safe approaches. Always use invokeLater when not definitely on the even processing thread, or use it after the first pack/setvisible.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Roedy Green - 03 Oct 2005 04:30 GMT >I suspect there are two safe approaches. Always use invokeLater when >not definitely on the even processing thread, or use it after the >first pack/setvisible. Oddly i discovered the answer in the Java glossary. I am in the process of separating out the Swing thread lore into its own entry.
Sun now recommends ALL Swing methods be called with invokeLater unless triggred from the event thread, even during the initial construction. They say you can leave old code as is, but all new code should follow that technique.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Roedy Green - 03 Oct 2005 02:06 GMT >I tried the invokeLater method The invokeLater method is defined as:
Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread.
Likely by some Bette Midler fan who set up the joke as carefully as any in Airplane II.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Anup - 03 Oct 2005 20:33 GMT > > There are basically two approaches: > > [quoted text clipped - 4 lines] > it spawned off. The trace shows that immediately after getting control, > the invoked-to methods are running under the same awt event thread. Let me elaborate. I renamed the init method of mainline to initSafe and created a new init method to invokeLater initSafe. The trace shows initSafe running under the awt event thread. Somewhere in the middle of its execution, initSafe spawns off another thread to a class FormFactory which is a subclass of Thread. I renamed the run method of FormFactory to runSafe and created a new init method to invokeLater runSafe. The trace shows runSafe running under the same awt event thread as initSafe. So as soon as I get control in the mailine or the spawned thread, I invokeLater to an awt event thread.
> > >3. What determines which awt event queue thread focus gain and lost > > >will operate under since they are invoked asynchronously. [quoted text clipped - 7 lines] > yet to do a detailed analysis of the trace to look for any > distinguishing features between the two. Ok, I did the detailed analysis. Initially, all the events are being serviced by awt event thread 1. Then for some reason, all the events get serviced by awt event thread 3. Then they revert back to thread 1. I know what I'm seeing but I don't know why.
Anup
Anup - 03 Oct 2005 23:44 GMT > > However, somewhere down the line I do see 2 awt event threads being > > active with different priority, even after using invokeLater. I have [quoted text clipped - 5 lines] > get serviced by awt event thread 3. Then they revert back to thread 1. > I know what I'm seeing but I don't know why. I did some more analysis. At the time one thread takes over from the other, some strange behavior does take place. When the higher priority thread took over, a caret was visible when it was not supposed to be. When the lower priority thread took control back it seemed like it was handling some events that were left over at the time the higher priority thread had intervened. So the 2 threads seem to be playing a part in the strange behavior I'm seeing. The trick is to get it down to just one thread.
Anup
Roedy Green - 04 Oct 2005 10:49 GMT >Ok, I did the detailed analysis. Initially, all the events are being >serviced by awt event thread 1. Then for some reason, all the events >get serviced by awt event thread 3. Then they revert back to thread 1. >I know what I'm seeing but I don't know why. We have to track down what is creating that bogus event thread.
Off the top of my head you need some code like this to tell you what threads exist.
Thread[] allThreads = new Thread[ Thread.activeCount()]; Thread.enumerate ( allThreads ); for (Thread t : allThreads ) { System.out.println( t.getName() ); }
Pepper your code with this or something more sophisticated and see if you can track down where that extra event thread is coming from.
I suspect it will be coming from a pack or setVisible(true ) not done with invokeLater. Check out main first, especially if you open two frames, check the second.
See http://mindprod.com/jgloss/swingthreads.html for background on the error you are trying to find.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Roedy Green - 04 Oct 2005 11:22 GMT >Thread[] allThreads = new Thread[ Thread.activeCount()]; >Thread.enumerate ( allThreads ); [quoted text clipped - 5 lines] >Pepper your code with this or something more sophisticated and see if >you can track down where that extra event thread is coming from. the sort of things you might do :
assert isOnlyOneEventThread(): new Throwable().printStackTrace();
if (Thread.activeCount() != prevActiveCount ) { new Throwable().printStackTrace(); dumpThreads(); prevActiveCount = Thread.activeCount(); }
of course you have to write boolean isOnlyOneEventThread and dumpThreads
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Anup - 04 Oct 2005 21:33 GMT > >Thread[] allThreads = new Thread[ Thread.activeCount()]; > >Thread.enumerate ( allThreads ); > >for (Thread t : allThreads ) > >{ > >System.out.println( t.getName() ); > >} Roedy, you'll never believe this. I printed out all the threads as you suggested. There are 2 thread groups - applet thread group and console writer thread group, each having an awt event thread. Java made a performance improvement to disallow blocking of console output by using the console writer thread group. Trouble is, java also seems to use this thread group for handling events. Just for experimentation, I set the option for "do not start console". With this option, the multiple caret situation disappeared. This is probably a java bug. In the meantime, I'm wondering if there is any way to disable the console writer thread group without disabling the console itself.
Anup
Roedy Green - 05 Oct 2005 00:11 GMT >Roedy, you'll never believe this. I printed out all the threads as you >suggested. There are 2 thread groups - applet thread group and console >writer thread group, With an Applet you have the problem all the different Applets can be running at once and they must share a common console, merging their outputs. The console is not really a console it is GUI windows something like a scrolling TextArea.
So it seems plausible it could have its own dispatch thread. However your Gui elements and your event handlers I would think would not have anything to do with it. However, it is conceivable that keystrokes are handed by it since your app could conceivably also field lines input from the console.
This is turning into a major expedition. We at least we are learning something interesting each day.
So things to try: 1. convert your Applet to an application hybrid by adding a main method. See http://mindprod.com/jgloss/applet.html for how. Run as an application to see if it behaves sanely. If it does, think if it will be possible to redeploy your app as a JAWS app.
2. Run your program as an Applet, and see WHICH event handlers get which thread. Is there any pattern?
3. Did you ever do a setVisible() or pack() on the console thread? It is just as naughty as any other non-Swing thread.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Anup - 05 Oct 2005 12:59 GMT > >Roedy, you'll never believe this. I printed out all the threads as you > >suggested. There are 2 thread groups - applet thread group and console > >writer thread group,
> So it seems plausible it could have its own dispatch thread. However > your Gui elements and your event handlers I would think would not have > anything to do with it. Theoretically, it shouldn't but the trace says otherwise.
> So things to try: > 2. Run your program as an Applet, and see WHICH event handlers get > which thread. Is there any pattern? It appears that the jvm started assigning events to the applet event thread and at some point in time also started assigning them to the console writer event thread (perhaps due to an overload?) regardless of component or event handler method. Since the console writer thread was at a higher priority, all of those events ran till exhausted before the applet event thread could run again.
> 3. Did you ever do a setVisible() or pack() on the console thread? It > is just as naughty as any other non-Swing thread. I did nothing directly with the console thread. I didn't even know it existed till the trace showed it.
Anup
Roedy Green - 05 Oct 2005 00:12 GMT > In the >meantime, I'm wondering if there is any way to disable the console >writer thread group without disabling the console itself. You could disable to console, but I don't see how it would co-ordinate all the applets without its own thread.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|