Duplicate :
|
Name: jk109818 Date: 03/28/2003 FULL PRODUCT VERSION : java version "1.4.1_01" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01) Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode) FULL OPERATING SYSTEM VERSION : Microsoft Windows 2000 [Version 5.00.2195] ADDITIONAL OPERATING SYSTEMS : Linux A DESCRIPTION OF THE PROBLEM : The problem occurs when an animated gif is displayed in a JEditorPane under text/html (e.g. img src=".."), and then the component is hidden. The GIF continues to eat CPU cycles, and the Image Animator thread lives on forever. Reference bug 4302818 (http://developer.java.sun.com/developer/bugParade/bugs/4302 818.html) which is the same problem, except with JLabel's and JButton's. The test case loads a JFrame with some buttons. One toggles appearance of a JLabel with an animated GIF, one toggles appearance of a JEditorPane with an animated GIF. The final button lists all the Image Animator threads. Toggling the JLabel in and out will create and destroy Image Animator threads as expected, but once the JEditorPane creates an Image Animator thread it never goes away. I do not include animated GIF's with this bug report, but the test case references some images on the web. Placing the JEditorPane in its own window that gets disposed has no effect, for simplicity I did not do that in the test case. The solution for bug #4302818 was to add "!isShowing() ||" in the if block in imageUpdate for JLabel. A similar statement was added to AbstractButton's imageUpdate, so that it returns false if the component is not visible. I tried this solution with JEditorPane but it was unsuccessful... this is (apparently) because the image observer for the animated gif in the HTML pane is not the JEditorPane itself. I am unsure what class manages the images references by HTML in editor panes. STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : 1. Create and display a JEditorPane referencing an animated image. 2. Hide and destroy the JEditorPane. 3. Note continued existence of an Image Animator thread. EXPECTED VERSUS ACTUAL BEHAVIOR : The Image Animator thread should go away once the component is hidden. ERROR MESSAGES/STACK TRACES THAT OCCUR : The Image Animator thread stack trace usually looks like the following: "Image Animator 0" daemon prio=4 tid=0x008FF0E8 nid=0x584 waiting on condition [d81f000..d81fd8c] at java.lang.Thread.sleep(Native Method) at sun.awt.image.GifFrame.dispose(Unknown Source) at sun.awt.image.GifImageDecoder.readImage(Unknown Source) at sun.awt.image.GifImageDecoder.produceImage(Unknown Source) at sun.awt.image.InputStreamImageSource.doFetch(Unknown Source) at sun.awt.image.ImageFetcher.fetchloop(Unknown Source) at sun.awt.image.ImageFetcher.run(Unknown Source) REPRODUCIBILITY : This bug can be reproduced always. ---------- BEGIN SOURCE ---------- import java.awt.Component; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.URL; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.ImageIcon; public class AnimTest extends JFrame { static final String IMAGE1 = "http://www.google.com/logos/google2.gif"; static final String IMAGE2 = "http://www.google.com/logos/giroux3.gif"; JButton labelButton; JButton paneButton; public AnimTest() { super("Testing Animated Icons"); getContentPane().setLayout(new FlowLayout()); // add animated label toggle button labelButton = new JButton("Toggle Label On"); labelButton.addActionListener(new MyLabelListener()); getContentPane().add(labelButton); // add animated label html pane toggle button paneButton = new JButton("Toggle Pane On"); paneButton.addActionListener(new MyPaneListener()); getContentPane().add(paneButton); // add thread dump button JButton threadButton = new JButton("Threads"); threadButton.addActionListener(new ThreadListener()); getContentPane().add(threadButton); } /** Add JLabel with text and animated icon. */ protected class MyLabelListener extends MyToggleListener { protected void prepareComponent() { component = new JLabel("Label"); try { ImageIcon icon = new ImageIcon(new URL(IMAGE1)); ((JLabel)component).setIcon(icon); } catch (java.net.MalformedURLException _ex) { _ex.printStackTrace(); } labelButton.setText("Toggle Label Off"); } protected void destroyComponent() { ((JLabel)component).setIcon(null); labelButton.setText("Toggle Label On"); } } /** Add JEditorPane with an img tag referencing an animated image. */ protected class MyPaneListener extends MyToggleListener { protected void prepareComponent() { String src = IMAGE2; JEditorPane imagePane = new JEditorPane(); imagePane.setContentType("text/html"); imagePane.setEditable(false); imagePane.setText ("<html><body>Pane<img src=\"" + src + "\"></body></html>"); component = imagePane; paneButton.setText("Toggle Pane Off"); } protected void destroyComponent() { JEditorPane imagePane = (JEditorPane) component; imagePane.setDocument (imagePane.getEditorKit().createDefaultDocument ()); imagePane.getEditorKit().deinstall(imagePane); paneButton.setText("Toggle Pane On"); } } protected abstract class MyToggleListener implements ActionListener { boolean toggle = true; Component component = null; public void actionPerformed(ActionEvent ae) { if (toggle) { // create component prepareComponent(); // add component getContentPane().add(component); toggle = false; } else { // clean up component destroyComponent(); // remove component getContentPane().remove(component); component = null; toggle = true; } validate(); repaint(); } /** Create component. */ protected abstract void prepareComponent(); /** Clean up component. */ protected abstract void destroyComponent(); } protected class ThreadListener implements ActionListener { public void actionPerformed(ActionEvent ae) { System.out.println("\nAnimator Threads\n---------------- "); Thread[] threads = new Thread[Thread.activeCount()+2]; Thread.enumerate(threads); for (int x=0; x < threads.length; x++) { if (threads[x] != null && threads[x].getName().indexOf ("Animator") > -1) { System.out.println(threads [x].getName()); } } } } public static void main(String[] args) { AnimTest frame = new AnimTest(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 200); frame.setLocation(200, 200); frame.show(); } } ---------- END SOURCE ---------- CUSTOMER WORKAROUND : Unknown. Overriding the imageUpdate in JEditorPane to return false when not visible does not work, since it isn't being called for animated GIF's referenced through HTML source in the pane. I don't know what class is being called. (Review ID: 180330) ======================================================================