2009-12-31 5 views
1

Я использую API в библиотеке Java, которая вызывается из потока отправки событий и требует, чтобы я возвращал полностью инициализированный компонент пользовательского интерфейса. Это выглядит следующим образом:Как подождать создания объекта в EDT без блокировки EDT?

public JDialog createDialog(); 

Но я могу только заполнить диалоговое окно после загрузки из базы данных, которая может занять 10 секунд иногда. Обычно я делал это в фоновом потоке, но поскольку этот метод вызывается из EDT, и поскольку мне нужно вернуть диалог, это не сработает. Это сторонняя библиотека, поэтому я не могу изменить метод, но есть ли что-нибудь, что я могу сделать, чтобы избежать блокировки EDT?

ответ

5

«Инициализировано» не обязательно совпадает с «Населенный». «Инициализировано» обычно означает, что объект полностью сконструирован, но может не иметь никаких данных. «Населенный», конечно, означает, что данные присутствуют и все задачи сбора данных завершены. Таким образом, можно предоставить вашей сторонней библиотеке полностью инициализированный JDialog без каких-либо данных.

Способ, которым я всегда хотел решить эту проблему, заключается в создании настраиваемого JDialog, который отображает сообщение занятости или индикатор выполнения или тому подобное, а затем запрашивает данные в другом потоке. Когда данные возвращаются, я заменяю сообщение «занято» данными (на EDT!). Что касается того, как вы должны выполнять свой запрос в фоновом потоке, я рекомендую использовать SwingWorkers. Мне нравится использовать приватный SwingWorker внутри моего настраиваемого JDialog, который обрабатывает запрос в методе doInBackground() и обрабатывает связанные с Display задачи в методе done(). Выполнение этого способа гарантирует, что связанные с отображением задачи будут выполняться только в EDT, а задачи, связанные с базой данных, будут выполняться только с помощью EDT. Если вы хотите получить достаточно хорошее представление об использовании SwingWorkers, ознакомьтесь с Sun's tutorial on worker threads. Простой пример был бы:

public class DBDIalog extends JDialog{ 
    private JLabel busyLabel = new JLabel("Fetching data from DataBase"); 

    public DBDialog(){ 
     //do your initialization stuff here 
    } 

    private class DBFetcher extends SwingWorker<Void,DBInfo>{ 

     @Override 
     protected DBInfo doInBackground() throws Exception{ 
      return fetchDataFromDB(); //or whatever database call to make 
     } 

     @Override 
     protected void done(){ 
      try{ 
       DBInfo info = get(); 
      //replace your busy label with your DBInfo 
      }catch(InterruptedException e){ 
       //do appropriate thread interrupted stuff 
      }catch(ExecutionException e){ 
       //do appropriate general error handling stuff 
      } 

     } 
    } 
} 

Несколько вещей, чтобы помнить, хотя: метод done() не является абстрактным, так что вы не обязаны переопределить его. Вы должны, однако. Если ваша реализация doInBackground() вызывает исключение, это исключение будет проглочено, если только done() не было отменено. Кроме того, не вносите изменений в свой графический интерфейс изнутри doInBackground(), если вы не используете SwingUtilities.invokeLater(Runnable), так как doInBackground() выполняется из другого потока, кроме EDT, и изменения графического интерфейса пользователя из фонового потока запрашивают странные и необъяснимые ошибки.

Когда это следует использовать? В отличие от других задач программирования, точка, в которой что-то слишком долго реагирует, намного короче в графических интерфейсах. Число, которое я обычно видел, записано около 250 мс. Если ваша задача занимает больше времени, она должна быть в фоновом потоке. В вашем случае, 10 секунд должны определенно быть в фоновом потоке, но тогда вы уже знали, что :)

EDIT:

Видя ваш комментарий, я вижу, что большая часть моего поста довольно спорный вопрос.Тем не менее, вы все равно можете использовать SwingWorker:

Попросите SwingWorker выполнить извлечение данных, а в методе done() постройте JDialog из данных и передайте это диалоговое окно в свою стороннюю библиотеку.

+0

Эта последняя часть о том, «что она построила JDialog из данных, а рука, что диалог ...» - это то, что сложно. Если мой метод вызывается в EDT, как я могу запустить что-то в фоновом режиме, а затем ждать его возвращения (чтобы я мог конструировать и возвращать диалог), не блокируя другие события, размещенные на EDT между тем, когда я вызываюсь и когда я вернусь? Похоже, что SwingWorker этого не решает. Тем не менее, ваши ответы были лучше из двух ответов. –

+0

Вы можете переопределить 'process()' для отображения промежуточных результатов и/или вызвать 'setProgress()' по мере необходимости. Вот пример: http://sites.google.com/site/drjohnbmatthews/randomdata – trashgod

1

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

С точки зрения пользователя, все, что займет 10 секунд от начала до завершения, будет проблемой. Лучше всего, если вы должны их что-то сразу, даже если оно не в окончательной форме. При необходимости вы можете поместить модальный диалог, который просто говорит «Загрузка».

+0

В соответствии с документацией для этой библиотеки, она должна быть заполнена полностью, когда я возвращаю ее (извините, если раньше я был неясно). Я не знаю, что произойдет, если я потом запомню его, но я думаю, это было бы плохо, поскольку они указывают, что он должен быть полностью заполнен. Другая документация для этого пакета, по-видимому, указывает на то, что я должен сначала загрузить данные, затем заполнить диалог и вернуть его, но я не знаю, как это сделать, не блокируя EDT. –

+0

* Scott Fines * дал вам хороший ответ: откройте фоновый процесс для загрузки данных, а затем в конце этого процесса создайте свой диалог (вернитесь на EDT). И чтобы пользователь не задавался вопросом, что происходит, покажите временный диалог, пока вы извлекаете данные. – kdgregory

 Смежные вопросы

  • Нет связанных вопросов^_^