我有一个Jframe,它是我的应用程序窗口(以下代码中的appFrame),其中包含很多逻辑,并且加载大约需要1秒左右的时间。同时,我想向我的用户展示一个非常不错的加载框架(initFrame)。但是,当我运行此代码时,initFrame确实会出现,但是它上的JLabel中的文本不会立即显示-实际上,在加载应用程序框架之前,它实际上根本不会出现。

如果我注释掉所有appFrame,并且仅启动initFrame,则立即加载文本,根本没有等待时间。为什么会这样呢?这可能是并发问题吗?

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() { //as per best practice for concurrency in swing - see http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
        @Override
        public void run() {
            final JFrame initFrame = new InitFrame();
            initFrame.setVisible(true);
            final AppFrame appFrame = new AppFrame();
            appFrame.setVisible(true);
            initFrame.setVisible(false);
            initFrame.dispose();
        }
    });

}

最佳答案

这是AppFrame中可能出问题的示例。

您可以使用线程运行测试:

java SplashTest true

有无
java SplashTest

启用线程后,您会看到SplashFrameAppFrame每250ms更新一次,或多或少。

如果未启用线程,则将看到带有且未显示组件的SplashFrame,应用程序“挂起”了4秒钟,然后看到了AppFrame

该示例有些人为设计,但可能会给您一些想法。

请注意,SplashFrameAppFrame没有“直接”连接。所有通信都是通过AppFrameWorkListener接口进行的。

我还将“工作”放在AppFrame中。但是实际上,如果有很多处理要做,应该将其从UI代码中提取出来,并在单独的Thread中运行,并且任务将以与当前AppFrame相同的方式通知SplashFrame进度。
import javax.swing.*;

class SplashTest {

    static boolean useThread = false;

    public static void main(String[] args) {
        // Pass true at the command line to turn on threading.
        // No args, or any value other than true will turn off threading.
        if (args.length > 0) {
            useThread = new Boolean(args[0]);
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SplashFrame splashFrame = new SplashFrame();
                splashFrame.setVisible(true);
                new AppFrame(splashFrame).setVisible(true);
            }});
    }

    private static class BaseFrame extends JFrame {
        public BaseFrame() {
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setSize(200, 200);
            setTitle(getClass().getSimpleName());
        }
    }

    private static class SplashFrame extends BaseFrame implements AppFrameWorkListener {
        JLabel status;

        public SplashFrame() {
            setLocation(0, 0);
            status = new JLabel("Splash Frame");
            getContentPane().add(status);
        }

        public void appFrameWorkStart() {
            status.setText("Work started");
        }

        public void appFrameWorkProgress(long timeElapsed) {
            status.setText("Work has taken " + timeElapsed + "ms so far");
        }

        public void appFrameWorkDone() {
            // http://stackoverflow.com/questions/1234912/how-to-programmatically-close-a-jframe
            setVisible(false);
            dispose();
        }
    }

    private static class AppFrame extends BaseFrame {
        JLabel status;
        AppFrameWorkListener listener;

        public AppFrame(AppFrameWorkListener listener) {
            setLocation(200, 200);
            status = new JLabel("App Frame");
            getContentPane().add(status);

            this.listener = listener;

            // None of this 'heavy lifting' should be in a constructor.
            if (useThread) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        doLotsOfWork(4);
                    }
                }).start();
            } else {
                doLotsOfWork(4);
                onWorkDone();
            }
        }

        private void doLotsOfWork(int workLengthSeconds) {
            // We're starting. Ensure onWorkStart is called on the EDT,
            // as this method may be called from a different Thread.
            invokeOnWorkStartOnEDT();

            long start = System.currentTimeMillis();

            // Hammer the CPU for "workLengthSeconds" number of seconds.
            // And do some contrived progress reporting.
            long workLengthMs = workLengthSeconds * 1000;
            while (System.currentTimeMillis() - start < workLengthMs) {
                long innerStart = System.currentTimeMillis();
                // Consume 250ms CPU before issuing progress update.
                while (System.currentTimeMillis() - innerStart < 250);
                invokeOnWorkProgressOnEDT(System.currentTimeMillis() - start);
            }

            // We're done now. Ensure onWorkDone is called on the EDT,
            // as this method may be called from a different Thread.
            invokeOnWorkDoneOnEDT();
        }

        private void invokeOnWorkStartOnEDT() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkStart();
                }
            });
        }

        private void invokeOnWorkProgressOnEDT(final long timeElapsed) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkProgress(timeElapsed);
                }
            });
        }

        private void invokeOnWorkDoneOnEDT() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkDone();
                }
            });
        }

        private void onWorkStart() {
            status.setText("Work Started");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkStart();
            }
        }

        private void onWorkProgress(long timeElapsed) {
            status.setText("Work has taken " + timeElapsed + "ms so far");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkProgress(timeElapsed);
            }
        }

        private void onWorkDone() {
            status.setText("Work Done");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkDone();
            }
        }
    }

    interface AppFrameWorkListener {
        public void appFrameWorkDone();
        public void appFrameWorkStart();
        public void appFrameWorkProgress(long timeElapsed);
    }

}

09-04 11:15