У меня есть большая фоновая задача с некоторыми промежуточными точками, где требуется ввод данных пользователем.
Например. buttonLoad_actionPerformed --> загрузить, проверить достоверность --> если устарело, спросить пользователя, следует ли обновлять. Если да --> обновите, если нет --> остановитесь.
На данный момент я закодировал это так:
// load worker
SwingWorker<Status,Object> swLoad=new SwingWorker<> () {
Validity doInBackGround() {
return load(file);
}
void done() {
Validity validity=get();
switch (validity) {
case UPTODATE:
return;
case OUTDATED:
int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
if (res != JOptionPane.YES_OPTION)
return;
// update worker
SwingWorker<Boolean,Object> swUpdate = new SwingWorker<>() {
Boolean doInBackGround() {
return update();
}
void done() {
Boolean success=get();
if (!success) ....
}
};
swUpdate.execute();
}
}
};
swLoad.execute();
Однако это может стать довольно нечитаемым, если требуется несколько шагов.
Каков наилучший подход для объединения условных/необязательных SwingWorkers?




Учитывая, что это немного неясный вопрос, я попробую.
Есть 2 пункта, которые выделяются для меня:
Простое решение для их улучшения — разделить код на отдельные методы. Например, переместите активный код из случая «УСТАРЕЛО» в отдельный метод. Конечно, это дольше, но легче следовать.
public loadInWorker() {
SwingWorker<Status,Object> swLoad=new SwingWorker<> () {
Validity doInBackGround() {
return load(file);
}
void done() {
onLoad(get());
}
}
};
swLoad.execute();
}
private onLoad(Validity validity) {
switch (validity) {
case UPTODATE:
return;
case OUTDATED:
int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
if (res == JOptionPane.YES_OPTION) {
updateInWorker();
}
}
}
private updateInWorker() {
SwingWorker<Boolean,Object> swUpdate = new SwingWorker<>() {
Boolean doInBackGround() {
return update();
}
void done() {
Boolean success=get();
if (!success) {
onUpdateFailure();
}
}
};
swUpdate.execute();
}
private onUpdateFailure() {
....
}
Редактировать + Мнение: Добавлю, что я никогда не использовал этот рабочий API. Глядя на него, он кажется довольно запутанным. В последние дни разработки на Java я полагался на ThreadPools для задач с коротким временем выполнения и на выделенные потоки для задач с длительным временем выполнения. Вы также можете написать оболочку для вызова SwingWorker, поскольку вам не нужны внутренние типы вне его. Пример использования болотных стандартных потоков:
public loadInThread() {
new Thread(this::loadInThread, "Load Thread").start();
}
private loadInThread() {
switch (load(file)) {
case UPTODATE:
return;
case OUTDATED:
int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
if (res == JOptionPane.YES_OPTION) {
new Thread(this::updateInThread, "Update Thread").start();
}
}
}
private updateInThread() {
if (!update()) {
// Update Failure code goes here
}
}
Я не понимаю, почему вы что-то делаете в своем методе done.
Validity doInBackGround() {
Validity validity = load(file);
switch (validity) {
case UPTODATE:
return validity;
case OUTDATED:
int res= confirmOnEdt();
if (res == JOptionPane.YES_OPTION) {
Boolean success = update();
//check the update and respond.
}
}
return validity;
}
public int confirmOnEDT() throws InterruptedException{
int[] container = {0};
SwingUtilities.invokeAndWait( ()->{
container[0] = JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
}
);
return container[0];
}
Один SwingWorker для полного набора задач. JOptionPane заблокирует ваш фоновый поток и будет ждать ввода. Кроме того, вы, вероятно, хотите, чтобы возвращаемое значение отличалось от Validity.
Разве JOptionPane.showConfirmDialog не следует называть в EDT и, следовательно, в done()?
Если вы должны вызвать его в EDT, тогда оберните звонок в SwingUtilities.invokeAndWait() Было бы полезно, если бы мы знали, чего вы хотите от результата в конечном итоге. У вас есть несколько этапов выполнения с обменом данными между EDT/GUI и вашим фоновым потоком. Способ, который вы предложили, действителен. Запустите нового Swing Worker для выполнения новой фоновой задачи. Хотя ваш пример не оправдывает этого.
Я отредактировал ответ, включив в него отображение диалогового окна в EDT. Вы можете видеть, что это также немного громоздко. Я думаю, что лучший дизайн — это знать свои данные. Графический интерфейс ссылается на ваши данные через модель. Ваш первый щелчок каким-то образом изменяет данные с помощью Swing Worker. Как только этот рабочий закончит работу, ваши данные перейдут в новое состояние. Затем вы ждете, пока графический интерфейс снова изменит данные. Для чего может потребоваться еще один свинг-воркер. Y может научиться избегать вложенности свинг-воркеров.
Я не должен звонить JOptionPane.showConfirmDialog в EDT. Я просто подумал, что лучше всего вызывать любое действие GUI в EDT. В том числе JOptionPane.
Смысл использования рабочего процесса Swing заключается в том, что состояние всех компонентов Swing должно обновляться в потоке отправки событий (EDT), а не в обычном потоке. Прочитайте учебник по Swing на Concurrency для получения дополнительной информации.