Java不是我的母语,我一直在与这个问题作斗争。
基本上,我发现直接从init()调用方法switchApplets()与从init()生成的新线程中调用它之间存在行为差异。
从新线程内部调用它的结果是新applet的屏幕显示 - 除非用户调整大小或最小化浏览器。如果在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;
}
}
我尝试使init()等待线程完成,以便我可以从那里调用switchApplets(),但这只会阻止EDT并阻止UI更新。还尝试使用SwingUtilities的invokeLater / invokeAndWait,但即使switchApplets()在EDT上运行,似乎必须直接从init()(或至少运行的线程init)调用它才能获得所需的效果
为什么在新线程中调用switchApplets()会导致稍微不同(和不需要的)UI行为?
答案 0 :(得分:0)
从新线程内部调用它的结果是新的applet白屏 - 直到/除非用户调整大小或最小化他们的浏览器。
尝试在错误的线程上执行UI代码可能会造成死锁。
我尝试使init()等待线程完成,以便我可以从那里调用switchApplets(),但这只会阻止EDT并阻止UI更新。
你走在正确的轨道上。您只需要从EDT调用switchApplets(),并且只能在另一个线程上完成工作。
在长时间运行的函数完成后,您确定在生成的线程中尝试使用invokeLater()或invokeAndWait()吗?自从我做applet以来已经有很长一段时间了,但是我不知道任何applet特定的原因,为什么它不起作用,并且它可以在任何其他情况下工作。即,
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线程上发生的事情。在实践中,你经常可以在没有它的情况下离开,但它很狡猾。您的进度更新可以使用SwingUtilities.invoke方法之一,也可以使用SwingWorker的机制;要么应该工作。 (SwingWorker本身提供了两种不同的方法:调用addPropertyChangeListener
(Swing线程)和setProgress
(后台线程),或调用publish
(后台线程)并覆盖process
(摇摆线程)。)
另外,一个小建议:如果处理一个检查过的异常是不方便的(或者不可能有效地这样做),而不是捕捉和忽略它,你至少应该抓住&amp;将其重新抛出为未经检查的例外:
catch (Exception e) {
throw new RuntimeException(e);
}
这样,任何异常的堆栈跟踪和错误消息都不会丢失。