当前位置: 首页 > 面试题库 >

WatchService和SwingWorker:如何正确执行?

宋铭
2023-03-14
问题内容

WatchService听起来像一个令人兴奋的主意…不幸的是,它看起来像在tutorial /
api中警告的那样低级,而且实际上不适合Swing事件模型(或者我错过了一些显而易见的东西,概率不为零)

以本教程中
WatchDir
示例中的代码(仅处理单个目录)为例,我基本上结束了

  • 扩展SwingWorker
  • 在构造函数中进行注册
  • 将无限循环放在doInBackground中等待键
  • 通过key.pollEvents()检索时发布每个WatchEvent
  • 通过使用已删除/创建的文件作为newValue触发propertyChangeEvents来处理块

    @SuppressWarnings("unchecked")
    

    public class FileWorker extends SwingWorker > {

    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);
            }
        }
    }
    

    }

基本思想是使使用代码的人幸福地不了解黏糊糊的细节:它侦听属性更改,并根据需要更新任意模型

    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的publish方法似乎不太合适:现在还可以,因为我只看一个目录(不想让Galopp朝错误的方向走:)尝试看几个目录时(如原始的WatchDir示例)有几个键,而WatchEvent相对于其中一个键。要解析路径,我需要事件和键正在监视的 目录 [A],但只能传递一个。不过,很可能逻辑分配错误

[A] 编辑 (由@trashgods的评论触发)-实际上,这不是我必须随事件传递的键,而是报告更改的目录。相应地改变了问题

仅供参考,此问题交叉发布到OTN摇摆论坛

附录

阅读WatchKey的api文档:

如果有多个线程从监视服务中检索信号键,则应注意确保仅在处理完对象的事件之后才调用reset方法。

似乎暗示这些事件应该

  1. 在与检索WatchKey相同的线程上进行处理
  2. 重置键后不应触摸

不确定,但结合(将来)要求递归地查看目录(多个)的决定(决定遵循@Eels建议),有点-很快就会发布我确定的代码

编辑 刚刚接受了我自己的回答-如果有人有合理的反对意见,我将谦虚地回复


问题答案:

实际上,@ Eels的评论并没有停止敲响我的脑袋-并最终注册:这是要走的路,但是不需要任何“人工”结构,因为我们已经 有了 完美的候选人-
这是PropertyChangeEvent本身:-)

从我的问题的总体过程描述中,前三个项目符号保持不变

  • 相同:扩展SwingWorker
  • 一样:在构造函数中做注册的事情
  • 相同:将无限循环放在doInBackground中等待键
  • 更改:通过key.pollEvents检索到的每个WatchEvent中创建适当的PropertyChangeEvent并发布PropertyChangeEvent
  • 已更改:在过程中触发先前创建的事件(块)

修改后FileWorker

@SuppressWarnings("unchecked")
public class FileWorker extends SwingWorker<Void, PropertyChangeEvent> {

    public static final String FILE_DELETED = StandardWatchEventKinds.ENTRY_DELETE.name();
    public static final String FILE_CREATED = StandardWatchEventKinds.ENTRY_CREATE.name();
    public static final String FILE_MODIFIED = StandardWatchEventKinds.ENTRY_MODIFY.name();

    // final version will keep a map of keys/directories (just as in the tutorial example) 
    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(createChangeEvent((WatchEvent<Path>) event, key));
            }

            // reset key return if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                break;
            }
        }
        return null;
    }

    /**
     * Creates and returns the change notification. This method is called from the 
     * worker thread while looping through the events as received from the Watchkey.
     * 
     * @param event
     * @param key
     */
    protected PropertyChangeEvent createChangeEvent(WatchEvent<Path> event, WatchKey key) {
        Path name = event.context();
        // real world will lookup the directory from the key/directory map
        Path child = directory.resolve(name);
        PropertyChangeEvent e = new PropertyChangeEvent(this, event.kind().name(), null, child.toFile());
        return e;
    }

    @Override
    protected void process(List<PropertyChangeEvent> chunks) {
        super.process(chunks);
        for (PropertyChangeEvent event : chunks) {
            getPropertyChangeSupport().firePropertyChange(event);
        }
    }
}


 类似资料:
  • 如何正确执行collect以获取作为键并将Ethernet对象作为每个流元素的值?

  • 问题内容: 我使用以下代码在Python中启动并关闭 在脚本结束执行后,我仍然在Mac活动监视器中找到的实例。实际上,每次我运行脚本时,都会创建一个新进程。 我应该如何关闭驱动程序? 问题答案: 不保证该方法释放与驱动程序实例关联的所有资源。请注意,这些资源包括但不限于驱动程序可执行文件(在这种情况下为PhantomJS)。该方法旨在释放驱动程序的所有资源,包括退出可执行进程。

  • 如何正确执行collect以获取作为键并将Ethernet对象作为每个流元素的值?

  • 问题内容: 我使用Java中的以下代码使用HMAC-SHA1哈希一些值: 属于 在PHP中,有一个类似的函数可用来比较Java实现返回的值。 因此,首先尝试是: 返回: 我的Java函数也会返回。 好的,看来可行。然后,我尝试使用一个更复杂的键: 返回: 这次,我的Java展示返回了: 我的PHP代码返回的哈希值不等于我的Java函数返回的值,而且我找不到原因。 有小费吗? 问题答案: 在您的PH

  • 问题内容: 朋友,我正在开发一个Java应用程序。多数民众赞成在性能监控。关于这一点,我在一堂课中获取价值,并在另一堂课中绘制图表。我想使用swingworker交替执行这两个课程。 上面的类用于收集服务器状态并将其存储在哈希图中。该类称为“ MySQLClass”。 上面的代码是在一个称为“ Graph”的类中绘制Graph的代码。我如何使用Swing Worker交替执行此操作并在每次迭代中绘

  • 问题内容: 情况: 我正在AngularJs中制作一个分配权限的应用程序。为了做到这一点,我有三个嵌套的ng-repeat。 第一循环 :显示PERMISSION GROUP 第二个循环 :对于每个权限组,显示“类别”。在此循环内执行一个函数,该函数将获取每个类别的所有子类别 第三循环 :显示子类别 问题: 问题在于第二个循环内函数的执行。 尝试1-ng-init: 在控制器中: 行为很奇怪。可能