2013-10-27 3 views
1

Java не мой родной язык, и я немного борюсь с этой проблемой.Applets - init(), EDT и темы

В принципе, я нахожу поведенческую разницу между вызовом метода switchApplets() непосредственно из init() и вызывая его из нового потока, порожденного init().

Последствием вызова изнутри нового потока является то, что новые апплетские whitescreens - до тех пор, пока пользователь не изменит размер или не минимизирует их браузер. Если вызывается в конце init(), новый пользовательский интерфейс отображает сразу без ввода пользователем. Но это не вариант, потому что он не ждет, пока поток завершит свою подготовительную работу.

код подстриженные-вниз:

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() (или, по крайней мере, инициализация потока работает), чтобы иметь желаемый эффект ,

Почему вызов switchApplets() из новой строки приводит к слегка отличающемуся (и нежелательному) поведению пользовательского интерфейса?

+0

Тем временем (или, возможно, постоянно) Я создаю новый класс из init(), который запускает JFrame в новом потоке и ждет его закрытия. – Eric

ответ

0

Последствием вызова изнутри нового потока является то, что новые апплет-аплеты - до тех пор, пока пользователь не изменит размер или не уменьшит свой браузер.

Скорее всего, это тупик, вызванный попыткой выполнить код пользовательского интерфейса в неправильной теме.

Я попытался сделать init() ждать завершения потока, чтобы я мог вызвать switchApplets() оттуда, но это только заблокировало EDT и не позволило обновить пользовательский интерфейс.

Вы на правильном пути. Вам нужно вызвать switchApplets() только с EDT, и только после того, как работа будет выполнена в другом потоке.

Вы уверены, что пытались использовать 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. На практике вы часто можете обойтись без него, но это изворотливо. Обновления вашего хода могут использовать один из SwingUtilities.вызывать методы или механизмы SwingWorker; либо должны работать. (Сама SwingWorker предоставляет два различных способа сделать это: Call addPropertyChangeListener (Swing нить) и setProgress (фоновый поток) или вызовите publish (фоновый поток) и переопределить process (Swing нить).)

Кроме того, небольшое предложение: если это неудобно иметь дело с проверяемым исключением (или невозможно с пользой сделать это), а не ловить и игнорируя его, вы должны по крайней мере поймать & Rethrow его как незарегистрированное исключение:

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

таким образом, в StackTrace и сообщение об ошибке любого исключения не будет потеряно.

+0

спасибо за то, что вы его нашли. При дальнейшем тестировании. SwingUtilities.invokeLater и SwingWorker приводят к той же проблеме, даже если я полностью вычеркнул init(), за исключением одного из них. EventQueue.invokeLater делает трюк, может быть, потому, что я расширяю апплет, а не JApplet. Тем не менее, EventQueue.invokeLater НЕ выполняет эту работу, если выполняется из нового потока, который я породил в своем исходном сообщении, или из-за SwingWorker, который до сих пор не позволяет мне выполнить длительные задания в первую очередь. – Eric

+0

@ Эрик Методы SwingUtilities.invoke и методы EventQueue.invoke делают то же самое. Один класс вызывает другой. Не могли бы вы попытаться выполнить работу из 'start()', а не 'init()'? Я думаю, они вызваны из той же темы, но у меня нет других идей. – Boann

+0

Документы говорят, что один является обложкой для другого, но SwingUtilities.invoke * явно отличается от EventQueue. Я попытаюсь взломать с помощью start() позже .. но я до сих пор не знаю, почему это так придирчиво к тому, из какого потока это уволили. – Eric