GUIクライアントTask設計
主流のGUIライブラリは単一スレッドを採用しており、JavaでもC++でもJavaのSwing/AWTとJFace/SWTはもちろん、
MVCの盛行、つまり観察者モード、あるいはイベント通知方式のため、GUIがマルチスレッドに設計されている場合、開発者は慎重に開発しなければならず、うっかりするとデッドロックやデッドロックサイクルが発生し、非常に使いにくく、開発者は基本的に気が狂う.
AWTも前期はマルチスレッドGUIライブラリにしたいと考えていたが,これによりインタフェースが速くなり,最終的に使いやすさで断念したという.
OK、遠くまで引っ張らないで、つまりGUIは単線ライブラリで、つまりすべてのGUI操作は同じGUIスレッド上で操作されて、同時操作の時、1つのキューが同期して並んでいます.
Swingの中でこの特殊なスレッドは有名なEDT(EventDispatchingThread)で、すべてのGUIの変更と相互間のイベント通知はこのスレッドを通じて配布されています.
Swingには、ブラウザが非正規のHTMLをサポートしているように、HTMLを書く人が正しい書き方に注意しなくなっているような、悪いフォールトトレランス設計があります.
Swingでは、GUI以外のスレッドで直接GUIコントロールを呼び出すことができ、その内部で現在のスレッドがEDTスレッドであるかどうかを確認し、そうでない場合、自動的にEDTキューに配布されます.
これにより、開発者がEDTスレッドに注目しない可能性があり、より多くの誤記を招く可能性があります.最後に、Swingが書いたGUIがカードだと文句を言っています.
例:
正しい書き方は次のとおりです.
一方、Eclipse用のJFace/SWTは、GUIスレッド以外でUIコントロールを呼び出すと、直接エラーを報告し、最初は面倒になりますが、より多くの問題を減らしました.
次のように呼び出さなければなりません.
非UIスレッドからUIを調整する問題は、Swing内部で転送されているため、それほど深刻ではないかもしれません.
しかし、逆にUIスレッド内で多くのことをすると、深刻になります.例えば、
GUIプロセスがブロックされ、Swingウィンドウ全体が詰まってしまい、ユーザーがインタフェースをクリックすると、インタフェースが応答しないことがわかります.
これは私たちが今日解決しなければならない問題です.JDK 1です.6以前のバージョンでは、
Swingはツールクラスを提供していないので、自分でスレッドまたはスレッドプールを起動してUI以外のロジックを操作することができます.
またはスレッドプールを使用します.
このような問題は、私が実行した後、インタフェースに実行した結果を表示する場合、
どうしようかな?もちろん最も直接的なのは
このようなコードは頭がくらくらしていて、いくつかの問題が解決されていません.
1.任务はばらばらで、统一管理がなくて、もし管理パネルをするならばどのように処理しますか?
2.任务はどのように取り消して、もし1年遅くても実行できない任务があったら、取り消したいのはどうしますか?
3.統一的なエラー処理はありませんか?エラーが発生したら、どこでも繰り返し処理します.
5.タスクの進捗状況、進捗バーを作成するにはどうすればいいですか?
6.タスクの実行プロセスにはイベント通知がありません.タスクの変化を傍受したい場合は、どのように処理しますか?
これらの問題に対して、JDK 1.6ついに、SwingWorkerというクラスを追加しました.APIを見てみましょう.
主な問題は次のとおりです.
1.ドキュメントを見ないと、その使い方を推測するのは難しいです.このクラスはエンティティドメイン、セッションドメイン、サービスドメインを一体化しており、非常に複雑です.つまり、タスク自体(エンティティドメイン)を表し、実行プロセスの状態(セッションドメイン)も含まれています.また、アクティブな実行能力(サービスドメイン)もあります.
2.署名上の2つの汎用型、1つ目は総タスクの戻り結果のタイプ、2つ目はサブタスクの戻り結果ですが、ほとんどの場合、サブタスクは使用できませんが、2つの汎用型を宣言する必要があります.
3.中には潜むルールも少なくありません.例えば、doInBackground()の戻り値はdone()メソッドに与えられ、get()メソッドで戻り値を取得します.
4.Title、Messageなどの記述情報がありません.
5.このクラスはThreadクラスと同様に1回しか実行できず、複数回の実行は無効であり、ステータスがあるため、これは正しい.
次のように使用されます.
JSR 296(Swing Application Framework)は上記の問題をいくつか修正しました.
TaskクラスがSwingWokerに継承されていると定義されていますが、徹底していません.JSR 296は原稿が決まっていません.現段階では使えません.
このような現状に基づいて、私たちは自分のTask案を拡張する必要があります.
インタフェースは次のように使用されます.
1.TaskSupport TaskインタフェースとTaskListenerインタフェース(エンティティドメイン)を同時に実現
2.タスク自体を表すTask(エンティティドメイン)
3.TaskListenerはイベント通知であり、すべてのUI操作はListenerで処理される(エンティティドメイン)
4.TaskContextは相互コンテキストであり、実行中の状態(セッションドメイン)を保存する
5.TaskEventはイベント情報(セッションドメイン)
6.TaskExecutorは実行能力を提供し、もちろんTaskExecutorはTaskServiceをSPIとして派生することができ、これによりSwing/AWTとJFace/SWT(サービスドメイン)を同時に互換化することができる.
インタフェースの署名は次のとおりです.
MVCの盛行、つまり観察者モード、あるいはイベント通知方式のため、GUIがマルチスレッドに設計されている場合、開発者は慎重に開発しなければならず、うっかりするとデッドロックやデッドロックサイクルが発生し、非常に使いにくく、開発者は基本的に気が狂う.
AWTも前期はマルチスレッドGUIライブラリにしたいと考えていたが,これによりインタフェースが速くなり,最終的に使いやすさで断念したという.
OK、遠くまで引っ張らないで、つまりGUIは単線ライブラリで、つまりすべてのGUI操作は同じGUIスレッド上で操作されて、同時操作の時、1つのキューが同期して並んでいます.
Swingの中でこの特殊なスレッドは有名なEDT(EventDispatchingThread)で、すべてのGUIの変更と相互間のイベント通知はこのスレッドを通じて配布されています.
Swingには、ブラウザが非正規のHTMLをサポートしているように、HTMLを書く人が正しい書き方に注意しなくなっているような、悪いフォールトトレランス設計があります.
Swingでは、GUI以外のスレッドで直接GUIコントロールを呼び出すことができ、その内部で現在のスレッドがEDTスレッドであるかどうかを確認し、そうでない場合、自動的にEDTキューに配布されます.
これにより、開発者がEDTスレッドに注目しない可能性があり、より多くの誤記を招く可能性があります.最後に、Swingが書いたGUIがカードだと文句を言っています.
例:
public static void main(String[] args) {
// UI
JFrame frame = new JFrame();
frame.show();
}
正しい書き方は次のとおりです.
public static void main(String[] args) {
// GUI , SwingUtilities.invokeLater
// EventQueue.invokeAndWait EventQueue.invokeLater
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
// EDT UI
JFrame frame = new JFrame();
frame.show();
}
});
}
一方、Eclipse用のJFace/SWTは、GUIスレッド以外でUIコントロールを呼び出すと、直接エラーを報告し、最初は面倒になりますが、より多くの問題を減らしました.
次のように呼び出さなければなりません.
// syncExec Swing invokeAndWait
// asyncExec Swing invokeLater
Display.getDefault().syncExec(new Runnable() {
public void run() {
// GUI
}
});
非UIスレッドからUIを調整する問題は、Swing内部で転送されているため、それほど深刻ではないかもしれません.
しかし、逆にUIスレッド内で多くのことをすると、深刻になります.例えば、
component.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// , , ...
}
});
GUIプロセスがブロックされ、Swingウィンドウ全体が詰まってしまい、ユーザーがインタフェースをクリックすると、インタフェースが応答しないことがわかります.
これは私たちが今日解決しなければならない問題です.JDK 1です.6以前のバージョンでは、
Swingはツールクラスを提供していないので、自分でスレッドまたはスレッドプールを起動してUI以外のロジックを操作することができます.
component.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
public void run() {
// , , ...
}
}).start();
}
});
またはスレッドプールを使用します.
component.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
threadPool.execute(new Runnable {
public void run() {
// , , ...
}
});
}
});
このような問題は、私が実行した後、インタフェースに実行した結果を表示する場合、
どうしようかな?もちろん最も直接的なのは
component.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
threadPool.execute(new Runnable() {
public void run() {
// , , ...
// final
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// UI
}
});
}
});
}
});
このようなコードは頭がくらくらしていて、いくつかの問題が解決されていません.
1.任务はばらばらで、统一管理がなくて、もし管理パネルをするならばどのように処理しますか?
2.任务はどのように取り消して、もし1年遅くても実行できない任务があったら、取り消したいのはどうしますか?
3.統一的なエラー処理はありませんか?エラーが発生したら、どこでも繰り返し処理します.
5.タスクの進捗状況、進捗バーを作成するにはどうすればいいですか?
6.タスクの実行プロセスにはイベント通知がありません.タスクの変化を傍受したい場合は、どのように処理しますか?
これらの問題に対して、JDK 1.6ついに、SwingWorkerというクラスを追加しました.APIを見てみましょう.
public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
// Runnable
void run();
// Future<T>
void cancel(boolean);
boolean isCancelled();
boolean isDone();
T get();
T get(long, TimeUnit);
// SwingWorker<T, V>
T doInBackground() throws Exception;
void publish(V...);
void process(List<V>);
void done();
void execute();
void setProgress(int);
int getProgress();
StateValue getState();
void addPropertyChangeListener(PropertyChangeListener);
void removePropertyChangeListener(PropertyChangeListener);
void firePropertyChange(String, Object, Object);
PropertyChangeSupport getPropertyChangeSupport();
}
主な問題は次のとおりです.
1.ドキュメントを見ないと、その使い方を推測するのは難しいです.このクラスはエンティティドメイン、セッションドメイン、サービスドメインを一体化しており、非常に複雑です.つまり、タスク自体(エンティティドメイン)を表し、実行プロセスの状態(セッションドメイン)も含まれています.また、アクティブな実行能力(サービスドメイン)もあります.
2.署名上の2つの汎用型、1つ目は総タスクの戻り結果のタイプ、2つ目はサブタスクの戻り結果ですが、ほとんどの場合、サブタスクは使用できませんが、2つの汎用型を宣言する必要があります.
3.中には潜むルールも少なくありません.例えば、doInBackground()の戻り値はdone()メソッドに与えられ、get()メソッドで戻り値を取得します.
4.Title、Messageなどの記述情報がありません.
5.このクラスはThreadクラスと同様に1回しか実行できず、複数回の実行は無効であり、ステータスがあるため、これは正しい.
次のように使用されます.
new SwingWorker<List<User>, Void> () { // , Void
// doInBackground , , get Future
protected List<User> doInBackground() throws Exception {
return remoteService.findUsers();
}
// EDT done , GUI done
protected done() {
try {
// get doInBackground
List<User> users = get();
// UI
tableModel.addAll(users);
} catch (Exception) {
// doInBackground , get
}
}
}.execute(); // , FutureTask
JSR 296(Swing Application Framework)は上記の問題をいくつか修正しました.
TaskクラスがSwingWokerに継承されていると定義されていますが、徹底していません.JSR 296は原稿が決まっていません.現段階では使えません.
このような現状に基づいて、私たちは自分のTask案を拡張する必要があります.
インタフェースは次のように使用されます.
TaskExecutor.execute(new TaskSupport() {
@TaskTitle(" ")
public void execute(TaskContext context) {
context.put("users", remoteService.findUsers());
}
public void succeeded(TaskEvent event) {
List<User> users = event.getTaskContext().get("users");
tableModel.addAll(users);
}
});
1.TaskSupport TaskインタフェースとTaskListenerインタフェース(エンティティドメイン)を同時に実現
2.タスク自体を表すTask(エンティティドメイン)
3.TaskListenerはイベント通知であり、すべてのUI操作はListenerで処理される(エンティティドメイン)
4.TaskContextは相互コンテキストであり、実行中の状態(セッションドメイン)を保存する
5.TaskEventはイベント情報(セッションドメイン)
6.TaskExecutorは実行能力を提供し、もちろんTaskExecutorはTaskServiceをSPIとして派生することができ、これによりSwing/AWTとJFace/SWT(サービスドメイン)を同時に互換化することができる.
インタフェースの署名は次のとおりです.
/**
*
*
* @author
*/
public interface Task {
/**
* .
* UI , , , , ,
* ,UI , TaskListner 。
*
* @param context
*/
void execute(TaskContext context);
}
/**
*
*
* @author
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TaskTitle {
/**
*
*
* @return
*/
String value();
}
/**
* ( , )
*
* @author
*/
public interface TaskContext {
/**
*
*
* @param key
* @param value
*/
void put(String key, Object value);
/**
*
*
* @param <T>
* @param key
* @return
*/
<T> T get(String key);
/**
*
*
* @param message
*/
void setMessage(String message);
/**
*
*
* @return
*/
String getMessage();
/**
*
*
* @param progress
*/
void setProgress(int progress);
/**
*
*
* @return
*/
int getProgress();
/**
*
*/
void cancel();
/**
*
*
* @return
*/
boolean isCancelled();
/**
*
*
* @return
*/
boolean isFailed();
/**
*
*
* @return
*/
boolean isSucceeded();
/**
*
*
* @return
*/
boolean isExecuted();
}
/**
* ( )
*
* @author
*/
public interface TaskService {
/**
*
*
* @param task
* @return
*/
TaskContext execute(final Task task);
}
/**
* ( )
*
* @author
*/
public class TaskExecutor {
//
private static TaskService TASK_SERVICE = new SwingTaskService();
/**
*
*
* @param taskService
*/
public static void setTaskService(TaskService taskService) {
if (taskService == null) {
throw new IllegalArgumentException("taskService == null");
}
TASK_SERVICE = taskService;
}
/**
*
*
* @param task
* @return
*/
public static TaskContext execute(Task task) {
return TASK_SERVICE.execute(task);
}
}
/**
*
*
* @author
*/
public interface TaskListener extends java.util.EventListener {
/**
*
*
* @param event
*/
void executing(TaskEvent event);
/**
* ( , , , )
*
* @param event
*/
void executed(TaskEvent event);
/**
*
*
* @param event
*/
void succeeded(TaskEvent event);
/**
*
*
* @param event
*/
void failed(TaskEvent event);
/**
*
*
* @param event
*/
void cancelled(TaskEvent event);
/**
*
*
* @param event
*/
void backgrounded(TaskEvent event);
/**
*
*
* @param event
*/
void foregrounded(TaskEvent event);
/**
*
*
* @param event
*/
void valueChanged(TaskEvent event);
/**
*
*
* @param event
*/
void messageChanged(TaskEvent event);
/**
*
*
* @param event
*/
void progressChanged(TaskEvent event);
}
/**
*
*
* @author
*/
public class TaskEvent extends java.util.EventObject {
private static final long serialVersionUID = -7251403985319158057L;
private final Task task;
private final TaskContext taskContext;
public TaskEvent(Object source, Task task, TaskContext taskContext) {
super(source);
this.task = task;
this.taskContext = taskContext;
}
/**
*
*
* @return
*/
public Task getTask() {
return task;
}
/**
*
*
* @return
*/
public TaskContext getTaskContext() {
return taskContext;
}
}
/**
*
*
* @author
*/
public class TaskAdapter implements TaskListener {
@Override
public void executing(TaskEvent event) {
}
@Override
public void executed(TaskEvent event) {
}
@Override
public void succeeded(TaskEvent event) {
}
@Override
public void failed(TaskEvent event) {
}
@Override
public void cancelled(TaskEvent event) {
}
@Override
public void backgrounded(TaskEvent event) {
}
@Override
public void foregrounded(TaskEvent event) {
}
@Override
public void valueChanged(TaskEvent event) {
}
@Override
public void messageChanged(TaskEvent event) {
}
@Override
public void progressChanged(TaskEvent event) {
}
}
/**
*
*
* @author
*/
public abstract class TaskSupport extends TaskAdapter implements Task {
}