Java 7 WatchService - 忽略同一事件的多次出现

标签 java file nio watchservice

StandardWatchEventKinds.ENTRY_MODIFY 的 javadoc说:

Directory entry modified. When a directory is registered for this event then the WatchKey is queued when it is observed that an entry in the directory has been modified. The event count for this event is 1 or greater.

当您通过编辑器编辑文件的内容时,它会同时修改日期(或其他元数据)和内容。因此,您会得到两个 ENTRY_MODIFY事件,但每个事件都有一个 count 1(至少这是我所看到的)。

我正在尝试使用以下代码监视手动更新(即通过命令行 servers.cfg )的配置文件( WatchService 之前注册到 vi ):

while(true) {
    watchKey = watchService.take(); // blocks

    for (WatchEvent<?> event : watchKey.pollEvents()) {
        WatchEvent<Path> watchEvent = (WatchEvent<Path>) event;
        WatchEvent.Kind<Path> kind = watchEvent.kind();

        System.out.println(watchEvent.context() + ", count: "+ watchEvent.count() + ", event: "+ watchEvent.kind());
        // prints (loop on the while twice)
        // servers.cfg, count: 1, event: ENTRY_MODIFY
        // servers.cfg, count: 1, event: ENTRY_MODIFY

        switch(kind.name()) {
            case "ENTRY_MODIFY":
                handleModify(watchEvent.context()); // reload configuration class
                break;
            case "ENTRY_DELETE":
                handleDelete(watchEvent.context()); // do something else
                break;              
        }
    }   

    watchKey.reset();       
}

既然你得到了两个 ENTRY_MODIFY事件,上面会在只需要一次时重新加载配置两次。假设可能有不止一个这样的事件,是否有任何方法可以忽略除其中之一之外的所有事件?

如果 WatchService API 有这样的实用程序就更好了。 (我有点不想检查每个事件之间的时间。我的代码中的所有处理程序方法都是同步的。

如果您从一个目录创建(复制/粘贴)文件到监视目录,也会发生同样的事情。如何将这两者结合为一个事件?

最佳答案

WatcherServices 报告事件两次,因为基础文件更新了两次。一次用于内容,一次用于文件修改时间。这些事件发生在很短的时间内。为了解决这个问题,在 poll()take() 调用和 key.pollEvents() 调用之间 hibernate 。例如:

@Override
@SuppressWarnings( "SleepWhileInLoop" )
public void run() {
  setListening( true );

  while( isListening() ) {
    try {
      final WatchKey key = getWatchService().take();
      final Path path = get( key );

      // Prevent receiving two separate ENTRY_MODIFY events: file modified
      // and timestamp updated. Instead, receive one ENTRY_MODIFY event
      // with two counts.
      Thread.sleep( 50 );

      for( final WatchEvent<?> event : key.pollEvents() ) {
        final Path changed = path.resolve( (Path)event.context() );

        if( event.kind() == ENTRY_MODIFY && isListening( changed ) ) {
          System.out.println( "Changed: " + changed );
        }
      }

      if( !key.reset() ) {
        ignore( path );
      }
    } catch( IOException | InterruptedException ex ) {
      // Stop eavesdropping.
      setListening( false );
    }
  }
}

调用 sleep() 有助于消除双重调用。延迟可能必须高达三秒。

关于Java 7 WatchService - 忽略同一事件的多次出现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16777869/

相关文章:

java - 如何在 SocketChannel 关闭时收到通知?

java - 使用 FileChannels 在 Java 中连接大文件的方法更有效

java - MyBatis - 找不到构造函数

java - 远程调试 Java 应用程序不显示变量值

file - Powershell读取主机用户输入添加

file - 我必须在 unix 中用其内容的第一个单词重命名所有文件名

Java - 以编程方式检查文件名中的无效字符

java - 如何检测流中的第一个文件

java - 如何使用数组而不是数组列表从java中的文件中逐行加载对象

java - Wakelock 并不总是让我的应用程序保持 Activity 状态