FULL PRODUCT VERSION :
java version "1.6.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.6.0-rc-b66)
Java HotSpot(TM) Client VM (build 1.6.0-rc-b66, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600] (with sp2)
A DESCRIPTION OF THE PROBLEM :
The JFileChooser is very slow to open (towards 10 seconds), at least the first time. Subsequent instantiations and navigation of the GUI are in some cases faster, and other cases not.
As suggested in bug 6317789, I tried setting "useShellFolder" to False, as in:
putClientProperty("FileChooser.useShellFolder", Boolean.FALSE);
This made the dialog appear "instantly", and all browsing is pretty much instant, with file sizes and all, but without the elaborate windows GUI.
Using JProfiler under the opening sequence, I found the following:
87.1% of the time used is in the JFileChooser.<init>. This is again broken down to 48.9% in .setup(), and 38.2% in .setCurrentDirectory(). These again both boil down through the following chain:
sun.awt.shell.ShellFolder.get
sun.awt.shell.Win32ShellFolderManager2.get
sun.awt.shell.Win32ShellFolder2.isDirectory
sun.awt.shell.Win32ShellFolder2.hasAttribute
sun.awt.shell.Win32ShellFolder2.getAttributes0()
.. with 35.3.% and 34.1% respectively. Thus, ~70% of the time is spent in this one method.
This happens both on the jdk 1.5 (cutting edge), and now also on the latest snapshot of 1.6.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Check out the attached source. This might not be a fantastic way to code, but it demonstrates some weirdness I at least experience consistently on my setup.
The three booleans will controll different "bug-aspects":
_fixChooser sets the useShellFolder to false
_windowsLookAndFeel sets the windows look and feel (giving the "elaborate" FileChooser)
_makeChooserInMain simply instantiates a JFileChooser in the main thread, and does nothing else with it.
If _fixChooser is true, the whole "application" is fast, both instantiations of JFileChooser and navigations of it, regardless of the other two booleans.
If _fixChooser is false, then both instantiations and navigation is very slow unless _makeChooserInMain is true. This is what I find very puzzling: if you make a JFileChooser in main before the main thread exits, then this will delay the startup by 7-8 seconds, but subsequent instantiations of the JFileChooser and all navigation is very fast.
The _windowsLookAndFeel does not affect the speed aspect at all.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Fast instantiation and GUI-navigation of the JFileChooser
ACTUAL -
Unusable slow, always in the first instantiation, and in certain circumstances for every "move" within the GUI and all subsequent instantiations.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
public class Test2 {
static boolean _fixChooser = false;
static boolean _windowsLookAndFeek = true;
static boolean _makeChooserInMain = true;
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
if (_windowsLookAndFeek) {
try {
UIManager.setLookAndFeel(new com.sun.java.swing.plaf.windows.WindowsLookAndFeel());
}
catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
if (_makeChooserInMain) {
getJFileChooser();
}
MyFrame myFrame = new MyFrame();
myFrame.startUp();
System.out.println("leaving main.");
}
static JFileChooser getJFileChooser() {
long startTime = System.currentTimeMillis();
System.out.println(Thread.currentThread() + " :: Before new JFileChooser()");
JFileChooser ret;
if (_fixChooser) {
ret = new JFileChooser() {
private static final long serialVersionUID = 1541813407103968847L;
@Override
public void updateUI() {
putClientProperty("FileChooser.useShellFolder", Boolean.FALSE);
super.updateUI();
}
};
}
else {
ret = new JFileChooser();
}
System.out.println(Thread.currentThread() + " :: After new JFileChooser(), took ["
+ (System.currentTimeMillis() - startTime) + "].");
return ret;
}
public static class MyFrame extends JFrame {
public void startUp() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
build();
setSize(new Dimension(100, 100));
setVisible(true);
}
});
}
public void build() {
Container contentPane = getContentPane();
JButton launchChooser = new JButton("launch chooser");
launchChooser.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
getJFileChooser().showDialog(null, "test!");
System.out.println(".showDialog exited.");
}
});
contentPane.add(launchChooser);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Make a JFileChooser instance in the main thread (which gives a one-time impact), or set the useShellFolder to false.