1. EDT (Event Dispatch Thread) nədir
Java-da qrafik tətbiqlərdə — istər Swing, istərsə də JavaFX — istifadəçinin bütün hərəkətləri (kliklər, düymələr), eləcə də pəncərələrin yenidən çəkilməsi EDT (Event Dispatch Thread, hadisələrin işlənməsi axını) adlı xüsusi axında emal olunur.
Nəyə görə lazımdır? Java-da UI komponentləri axın‑təhlükəsiz deyil. Yarış şəraitini və artefaktları önləmək üçün interfeysə edilən bütün dəyişikliklər yalnız bir yerdə — EDT-də icra olunur. Bu, tək kassalı kassaya bənzəyir: eyni qəbzlə eyni anda bir neçə nəfər məşğul ola bilməz.
Swing-də EDT hadisə emalçılarını və komponentlərin yenidən çəkilməsini işə salır (məsələn, actionPerformed). JavaFX-də analoqu — JavaFX Application Thread — burada UI yeniləmələri və setOnAction kimi emalçılar icra olunur.
2. UI-də “uzun əməliyyatlar” problemi
EDT-də uzun bir əməliyyat işə salınsa nə baş verəcək?
İstifadəçi düyməni basanda emalçı (məsələn, actionPerformed və ya setOnAction) EDT-də icra olunur. Əgər daxilində ağır tapşırıq (böyük faylın oxunması, şəbəkə sorğusu, mürəkkəb hesablamalar) işə salırsınızsa, bütün UI “donur”:
- Pəncərə kliklərə və düymələrə reaksiya vermir.
- Yenidən çəkilmə dayanır — pəncərəni hərəkət etdirəndə görüntü “donub qalır”.
- İstifadəçi proqramın “xarab olduğunu” düşünür.
Səhv kod nümunəsi (Swing):
button.addActionListener(e -> {
// Uzun əməliyyat birbaşa EDT-də!
longOperation(); // Məsələn, böyük faylın oxunması
label.setText("Hazırdır!");
});
Nəticə: longOperation() icra olunduğu müddətdə pəncərə istifadəçiyə cavab vermir.
Niyə belə olur? EDT tapşırıqları növbə ilə emal edir və eyni anda yalnız birini yerinə yetirə bilər. O, uzun əməliyyatla məşğul ikən nə klikləri, nə də yenidən çəkilməni işləyə bilir.
3. Həll: uzun əməliyyatlar — yalnız fon axınlarında
Prinsip:
- Bütün uzun əməliyyatlar — yalnız fon axınlarında.
- Bütün UI dəyişiklikləri — yalnız EDT/JavaFX Application Thread-də.
Uzun əməliyyatı ayrıca axında işə salaq
Nümunə (Swing):
button.addActionListener(e -> {
new Thread(() -> {
longOperation(); // Fon axınında icra olunur
// İndi UI-ni yeniləmək lazımdır — amma yalnız EDT-dən!
SwingUtilities.invokeLater(() -> label.setText("Hazırdır!"));
}).start();
});
Nümunə (JavaFX):
button.setOnAction(e -> {
new Thread(() -> {
longOperation();
// UI-ni Platform.runLater vasitəsilə yeniləyirik
Platform.runLater(() -> label.setText("Hazırdır!"));
}).start();
});
UI-ni fon axınından necə yeniləmək olar?
- Swing: SwingUtilities.invokeLater(Runnable)-dən istifadə edin — tapşırıq EDT növbəsinə düşəcək.
- JavaFX: Platform.runLater(Runnable)-dən istifadə edin — tapşırıq JavaFX Application Thread-də icra olunacaq.
Niyə sadəcə label.setText(...) fon axınından çağırmaq olmaz? Çünki bu, UI-nin axın təhlükəsizliyini pozur: komponentlər yalnız interfeys axınından dəyişdirilməlidir.
Fon tapşırıqları üçün xüsusi siniflər
Real tətbiqlərdə tez-tez proqresi göstərmək, ləğv etməyə icazə vermək və səhvləri emal etmək lazım olur. Bunun üçün aşağıdakılar var:
- SwingWorker<T, V> — Swing üçün;
- Task<V>, Service<V> — JavaFX üçün.
Nümunə (JavaFX Task):
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
longOperation();
// Proqresi yeniləmək olar: updateProgress(...)
return null;
}
};
task.setOnSucceeded(e -> label.setText("Hazırdır!"));
task.setOnFailed(e -> label.setText("Xəta!"));
new Thread(task).start();
Üstünlüklər: proqres, ləğv etmə, uğur/xəta hadisələri. UI dəyişiklikləri — təhlükəsiz metodlar (updateMessage, updateProgress) və ya emalçılar (setOnSucceeded və s.) vasitəsilə.
4. Düzgün və yanlış nümunələr
Yanlışdır: uzun əməliyyatları hadisə emalçısında icra etmək
button.setOnAction(e -> longOperation()); // UI donacaq!
Doğrudur: uzun əməliyyatlar ayrıca axında
button.setOnAction(e -> new Thread(() -> longOperation()).start());
Daha da yaxşısı: Task/Worker istifadə etmək
JavaFX:
button.setOnAction(e -> {
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
longOperation();
return null;
}
};
task.setOnSucceeded(ev -> label.setText("Hazırdır!"));
new Thread(task).start();
});
Swing:
button.addActionListener(e -> {
SwingWorker<Void, Void> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
longOperation();
return null;
}
@Override
protected void done() {
label.setText("Hazırdır!");
}
};
worker.execute();
});
5. Təcrübə: fayl yükləmə nümunəsi
JavaFX:
button.setOnAction(e -> {
Task<String> task = new Task<>() {
@Override
protected String call() throws Exception {
// Uzun yükləmənin imitasiyası
Thread.sleep(2000);
return "Fayl yükləndi!";
}
};
task.setOnSucceeded(ev -> label.setText(task.getValue()));
new Thread(task).start();
});
Swing:
button.addActionListener(e -> {
SwingWorker<String, Void> worker = new SwingWorker<>() {
@Override
protected String doInBackground() throws Exception {
Thread.sleep(2000);
return "Fayl yükləndi!";
}
@Override
protected void done() {
try {
label.setText(get());
} catch (Exception ex) {
label.setText("Xəta!");
}
}
};
worker.execute();
});
6. EDT və uzun əməliyyatlarla işləyərkən tipik səhvlər
Səhv №1: Uzun əməliyyatın EDT-də icrası. Bütün tətbiq “donur”, pəncərə reaksiya vermir, istifadəçi proqramın sıradan çıxdığını düşünür.
Səhv №2: UI-ni fon axınından yeniləməyə cəhd. UI-nin axın təhlükəsizliyinin pozulması xətalara, artefaktlara və çökmələrə səbəb ola bilər. SwingUtilities.invokeLater və ya Platform.runLater istifadə edin.
Səhv №3: Fon tapşırığında səhvlərin emal olunmaması. İstisnalar “itirilir”, istifadəçi nəyi səhv getdiyini bilmir. Swing-də — done()-u override edin və get()-i oxuyun; JavaFX-də — setOnFailed-ə abunə olun.
Səhv №4: Uzun əməliyyatı ləğv etmək imkanı yoxdur. İstifadəçi yükləməni/hesablamanı dayandıra bilmir. Ləğv etmə dəstəyindən istifadə edin (SwingWorker.cancel, Task.cancel) və tapşırıq daxilində ləğv bayraqlarını yoxlayın.
Səhv №5: Proqres göstəricisi yoxdur. İstifadəçi proqramın “donduğunu” düşünür. Swing-də — nəticələrin dərcini və proqres çubuğunu SwingWorker ilə birlikdə istifadə edin; JavaFX-də — updateProgress və vizual indikatorlardan istifadə edin.
GO TO FULL VERSION