3

アプリケーションのウィンドウ (次のコードでは appFrame) である Jframe があります。これには多くのロジックが含まれており、読み込みに 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();
        }
    });

}
4

3 に答える 3

3

フレームの作成を 2 つのスレッドに分けます。最初に、初期化しますInitFrame。このスレッドを実行し、オブジェクトでisShowing()InitFrameを呼び出します。true が返されたら、2 番目のスレッドを実行して初期化して表示しAppFrameます。

これにより、2 つのフレームの可視性の間に発生前の関係が強制されます。

class Main {
    JFrame initFrame = null;
    AppFrame appFrame = null;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
               initFrame = new InitFrame();
               initFrame.setVisible(true);
            }
        });

        while(!initFrame.isShowing()) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
            }
        }

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                appFrame = new AppFrame();
                appFrame.setVisible(true);
                initFrame.setVisible(false);
                initFrame.dispose();
            }
        });

    }
}
于 2013-05-02T19:29:07.463 に答える
2

で問題が発生している可能性のある例を次に示しますAppFrame

スレッドでテストを実行できます。

java SplashTest true

またはなし

java SplashTest

スレッド化を有効にすると、SplashFrameAppFrameがほぼ 250 ミリ秒ごとに更新されます。

スレッド化が有効になっていない場合、コンポーネントが表示SplashFrameされに が表示され、アプリが 4 秒間「ハング」した後、 が表示されますAppFrame

この例はやや不自然ですが、いくつかのアイデアが得られるかもしれません。

SplashFrameには への「直接」接続がないことに注意してくださいAppFrame。すべての通信はAppFrameWorkListenerインターフェイスを介して行われます。

「作品」も入れましたAppFrameThreadしかし実際には、実行する処理が大量にある場合は、UI コードから抽出し、別の で実行する必要があります。現在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);
    }

}
于 2013-05-02T20:53:11.217 に答える
1

Javaスレッドを使用する必要があり、インタラクティブなスプラッシュスクリーン(カスタムメイド)をユーザーに表示できますが、コードが必要なものを生成している間、ここで必要なものはチュートリアルです.

優れた効率的な同時実行のためにスレッドを使用する必要があります

于 2013-05-02T19:38:33.293 に答える