WatchService звучало как увлекательная идея ... к сожалению, она кажется настолько низкой, как было предупреждено в руководстве / api plus, она не совсем вписывается в модель событий Swing (или я упускаю что-то очевидное, не нулевая вероятность
Взяв код из примера WatchDir в учебном пособии (просто для обработки только одного каталога), я в итоге получился
- продление SwingWorker
- сделать регистрацию вещей в конструкторе
- положить бесконечный цикл ожидания ключа в doInBackground
- публиковать каждое WatchEvent при получении через key.pollEvents ()
обработать чанки, запустив propertyChangeEvents с удаленными / созданными файлами как newValue
@SuppressWarnings("unchecked")
public class FileWorker extends SwingWorker<Void, WatchEvent<Path>> {
public static final String DELETED = "deletedFile";
public static final String CREATED = "createdFile";
private Path directory;
private WatchService watcher;
public FileWorker(File file) throws IOException {
directory = file.toPath();
watcher = FileSystems.getDefault().newWatchService();
directory.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
}
@Override
protected Void doInBackground() throws Exception {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return null;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
publish((WatchEvent<Path>) event);
}
// reset key return if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
break;
}
}
return null;
}
@Override
protected void process(List<WatchEvent<Path>> chunks) {
super.process(chunks);
for (WatchEvent<Path> event : chunks) {
WatchEvent.Kind<?> kind = event.kind();
Path name = event.context();
Path child = directory.resolve(name);
File file = child.toFile();
if (StandardWatchEventKinds.ENTRY_DELETE == kind) {
firePropertyChange(DELETED, null, file);
} else if (StandardWatchEventKinds.ENTRY_CREATE == kind) {
firePropertyChange(CREATED, null, file);
}
}
}
}
Основная идея состоит в том, чтобы сделать использование кода блаженно неосведомленным о слизистых деталях: он прислушивается к изменениям свойств и f.i. обновляет произвольные модели по мере необходимости:
String testDir = "D:\\scans\\library";
File directory = new File(testDir);
final DefaultListModel<File> model = new DefaultListModel<File>();
for (File file : directory.listFiles()) {
model.addElement(file);
}
final FileWorker worker = new FileWorker(directory);
PropertyChangeListener l = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (FileWorker.DELETED == evt.getPropertyName()) {
model.removeElement(evt.getNewValue());
} else if (FileWorker.CREATED == evt.getPropertyName()) {
model.addElement((File) evt.getNewValue());
}
}
};
worker.addPropertyChangeListener(l);
JXList list = new JXList(model);
Кажется, работает, но я чувствую себя некомфортно
- Я представляю себя независимым от потока: все примеры фрагментов, которые я видел до сих пор, блокируют ожидающий поток с помощью watcher.take (). Почему они это делают? Ожидается, по крайней мере, некоторые используют watcher.poll () и немного поспать.
- метод публикации SwingWorker, похоже, не совсем подходит: сейчас все в порядке, так как я смотрю только один каталог (не хотел слишком быстро скакать в неправильном направлении :) При попытке просмотра нескольких каталогов ( как в исходном примере WatchDir) есть несколько ключей и WatchEvent относительно одного из них. Чтобы определить путь, мне понадобится как событие, так и каталог [A], за которым наблюдает ключ, но он может передать только одно. Скорее всего, неправильное распределение логики, хотя
[A] Отредактировано (вызвано комментарием @ trashgods) - это на самом деле не ключ, который я должен передавать вместе с событием, это каталог, в котором сообщается об изменениях. Изменил вопрос соответственно
К вашему сведению, этот вопрос находится в перекрестном сообщении на OTN Swing форуме
Добавление
Чтение API-интерфейса WatchKey:
Там, где есть несколько потоков, извлекающих сигнальные ключи из часов
затем следует позаботиться о том, чтобы метод сброса
вызывается только после обработки событий для объекта.
, кажется, подразумевает, что события должны
- обрабатываться в том же потоке, который получил WatchKey
- не следует трогать после сброса ключа
Не совсем уверен, но в сочетании с (будущим) требованием рекурсивного просмотра каталогов (более одного) решил последовать совету @Eels, вроде - скоро опубликую код, на котором я остановился
EDIT
только что принял мой собственный ответ - смиренно вернет, что если у кого-то есть разумные возражения