1

Java は私の母国語ではなく、しばらくこの問題と戦ってきました。

基本的に、メソッド switchApplets() を init() から直接呼び出す場合と、init() によって生成された新しいスレッド内から呼び出す場合の動作の違いを見つけています。

新しいスレッド内から呼び出した結果、ユーザーがブラウザのサイズを変更または最小化するまで、またはしない限り、新しいアプレットはホワイトスクリーンになります。init() の最後に呼び出された場合、新しい UI はユーザーからの入力なしですぐにレンダリングされます。しかし、スレッドが準備作業を完了するのを待たないため、これはオプションではありません。

短縮されたコード:

public class PreLoader extends Applet implements AppletStub {

static JProgressBar pBar = null;
static JLabel message;

public void switchApplets() {
    try {
        Class main_class = Class.forName("MainClass");
        Applet main_applet = (Applet)main_class.newInstance();
        removeAll();
        setSize(0,0);
        setLayout(new GridLayout(1,0));
        add(main_applet);
        main_applet.init();
        main_applet.start();
        main_applet.setStub(this);
    }
    catch (Exception e) {
    }
}

public void init() {

    pBar = new JProgressBar(0, 100);
    pBar.setValue(0);
    pBar.setStringPainted(true);

    message = new JLabel("Beginning work!");

    add(message);
    add(pBar);

    FlowLayout flow = new FlowLayout();

    setLayout(flow);

    Thread t = new Thread ( new Runnable () {
        public void run ()
        {
            longRunningFunction1();
            longRunningFunction2();
            message.setText("Work complete! Stand by..");
            switchApplets(); //does NOT work as intended from here
            return;
        }
    } );
    t.start();
    //switchApplets(); //works as intended if called HERE
}

public void longRunningFunction1() {
    //perform some tasks, advance progress bar
}

public void longRunningFunction2() {
    //perform some tasks, advance progress bar
}

public void start() {
    return;
}

public void appletResize(int width, int height) {
    return;
}

}

そこから switchApplets() を呼び出すことができるように init() をスレッドが終了するまで待機させようとしましたが、それは EDT をブロックするだけで、UI の更新を妨げました。また、SwingUtilities の invokeLater/invokeAndWait で遊んでみましたが、switchApplets() は EDT で実行されますが、init() から直接呼び出す必要があるようです (または、少なくとも init が実行されているスレッド)。 .

新しいスレッド内から switchApplets() を呼び出すと、わずかに異なる (そして望ましくない) UI 動作が発生するのはなぜですか?

4

1 に答える 1

0

新しいスレッド内から呼び出した結果、ユーザーがブラウザのサイズを変更または最小化するまで、またはしない限り、新しいアプレットはホワイトスクリーンになります。

間違ったスレッドで UI コードを実行しようとしたためにデッドロックが発生した可能性があります。

そこから switchApplets() を呼び出すことができるように init() をスレッドが終了するまで待機させようとしましたが、それは EDT をブロックし、UI の更新を妨げただけでした。

あなたは正しい軌道に乗っています。EDT からのみ switchApplets() を呼び出す必要があり、他のスレッドで作業が完了した後にのみ呼び出します。

実行時間の長い関数が実行された後、生成されたスレッド内から invokeLater() または invokeAndWait() を使用しようとしたことは確かですか? アプレットを作成してから長い時間が経ちましたが、それが機能しないアプレット固有の理由はわかりません。それ以外の場合は機能します。すなわち、

public void run()
{
    longRunningFunction1();
    longRunningFunction2();
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            message.setText("Work complete! Stand by..");
            switchApplets();
        }
    });
}

ただし、これを行う最も適切な方法は、手動で作成されたスレッドではなく、 SwingWorkerを使用することです。SwingWorker (それほど有名ではありません) は、進行状況の更新と結果で GUI を更新しながら、別のスレッドでバックグラウンド タスクを実行することを目的として設計されています。例えば、

new SwingWorker<Void,Void>() {
    @Override
    protected Void doInBackground() { // is called on a background thread
        longRunningFunction1();
        longRunningFunction2();
        return null;
    }

    @Override
    protected void done() { // is called on the Swing thread
        message.setText("Work complete! Stand by..");
        switchApplets();
    }
}.execute();

これVoidは、SwingWorker が結果を返し、中間の進行状況の更新を送信することもできるためですが、この例ではそれらの機能を使用していません。

長時間実行される関数も進行状況バーを更新していることを示しました。これは、Swing スレッドでのみ発生するはずのもう 1 つのことです。実際には、それなしで済むことがよくありますが、危険です。進行状況の更新では、SwingUtilities.invoke メソッドのいずれか、または SwingWorker のメカニズムを使用できます。どちらかが動作するはずです。addPropertyChangeListener(SwingWorker 自体は、呼び出し(Swing スレッド) とsetProgress(バックグラウンド スレッド)、または呼び出しpublish(バックグラウンド スレッド) とオーバーライド(Swing スレッド)の 2 つの異なる方法を提供しますprocess。)

また、小さな提案: チェックされた例外を処理するのが不便な場合 (または有効に処理することが不可能な場合) は、それをキャッチして無視するのではなく、少なくとも未チェックの例外としてキャッチして再スローする必要があります。

catch (Exception e) {
    throw new RuntimeException(e);
}

そうすれば、例外のスタック トレースとエラー メッセージが失われることはありません。

于 2013-10-28T04:47:39.637 に答える