虚拟文件系统
虚拟文件系统
虚拟文件系统 ( Virtual File System:缩写为VFS) 是 IntelliJ 平台的一个组件,它封装了虚拟文件 的大部分操作。
VFS主要有以下功能:
用统一的API来处理不同来源的文件(例如:硬盘上的文件,jar包中的文件,http文件等)
监控文件的修改,为文件提供版本控制
管理文件的持久化操作
在后2个功能里,VFS管理了持久化文件的一个快照。快照里只存储了通过VFS API请求过的文件,并且能同步更新到硬盘上。
快照是应用级的,不是项目级的,所以即使一个文件(例如。JDK中的类)被多个项目引用了,在VFS系统里也只会存储一份
所有VFS的操作都会经过快照
通过 VFS 请求一些数据,如果快照中的数据是可用的,将直接返回快照中的数据,如果快照中没有可用数据,将会从磁盘中加载并存储到VFS 里。仅当访问特定信息时,快照才会存储文件的内容和目录中的文件列表。否则,只存储文件元数据,如名称、长度、时间戳、属性等。
提示
在 IntelliJ Platform UI中显示的文件内容来源于快照,这将导致显示中的内容和硬盘上的内容并不会永远完全一致。例如:某些文件被删除了,但是IntelliJ Platform 还没有删除它的快照,所以还能访问该文件。
在磁盘刷新操作(refresh operations)的时候快照会进行 同步 更新。所有通过VFS的写入操作都是同步 的,保存的时候会立即更新到硬盘上。
IntelliJ Platform或插件在调用刷新操作(refresh operations)时,刷新操作(refresh operations)才会将硬盘内容和VFS 的相关内容进行同步。 也就是说,当IDE 在运行的时候,硬盘上的文件被修改了。VFS 并不会立即更新,VFS 会在下一次刷新操作(refresh operations)的时候更新文件,以及跟该文件关联的其它文件。
IntelliJ Platform会在 IDE 启动的时候异步 刷新项目内容。默认情况下,用户从其它应用切换到IDE 的时候会进行刷新操作。用户也可以通过 Settings/Preferences | Appearance & Behavior | System Settings | Synchronize external changes[...] 关闭默认刷新操作,如下图:
在Windows, Mac, 和 Linux系统上,会启动一个监控进程来监控文件的修改并通知IntelliJ Platform。如果监控进程可用,IntelliJ Platform 刷新操作只会更新通知的文件,如果监控进程不可用,刷新操作会遍历更新相关的所有文件和文件夹
提示
在内部操作 Tools | Internal Actions | VFS 可以看到VFS的相关信息
刷新操作是依据时间戳进行对比的。如果文件的内容已更改,但其时间戳保持不变,则 IntelliJ Platform 将不会更新内容。
快照不支持删除某个文件。如果加载过某个文件,只有从硬盘上删除这个文件,并且它的父目录调用了刷新操作,才会从快照中删除该文件,否则这个文件会一直保存在快照中。
VFS 不会加载在 Settings/Preferences | Editor | File Types和 Project Structure | Modules | Sources | Excluded 忽略的文件和文件夹。但是当应用程序代码访问它们的时候,VFS 会加载它们。
在IntelliJ Platform IDE 运行期间,大部分的 VirtualFile 和硬盘上的文件内容一样,拥有相同的 hashCode,共享用户数据。
同步和异常刷新
从调用者的角度看,刷新操作分为同步和异步。实际上,刷新操作是按照自己的策略执行。
同步刷新操作会阻塞线程直到刷新操作(刷新操作大部分情况在另外的线程进行)完成。
任何线程都可以进行同步刷新和异步刷新。但是后台线程在进行读取操作时,不能调用刷新操作,因为会引起死锁。详情可查看文档:线程规则
相同的线程要求也适用于 LocalFileSystem.refreshAndFindFileByPath() 等函数,如果在快照中找不到指定路径的文件,则会刷新指定路径的内容
强烈推荐使用异步刷新操作。如果需要在刷新操作完成后执行业务逻辑,可以在 RefreshQueue.createSession() 和 VirtualFile.refresh() 方法的 postRunnable 参数里执行业务逻辑
使用同步刷新操作可能会引起死锁
虚拟文件系统事件
VFS 进行刷新操作、用户进行操作等,都会发布VFS 事件。VFS事件一般都由写入操作和dispatch thread 触发。
想要监听VFS 事件,最好的方式是实现 BulkFileListener 接口,并在接口中订阅 VirtualFileManager.VFS_CHANGES 主题。 在IntelliJ Platform 2019.2 及以后的版本里,也可以使用非阻塞监听器 AsyncFileListener ,如何实现监听功能请查看文档 订阅VFS文件修改事件
注意
VFS 监听器是应用级的,会接收所有项目里发生的文件修改事件。可以通过 ProjectFileIndex.isInXX() 对事件进行过滤
每一次文件修改前、文件修改后都会发送 VFS 事件, 可以从文件修改前发送的事件中获取文件未修改时的内容。需要注意的是通过VFS 修改文件时,当刷新操作完成,并更新到磁盘上后,才会发送文件修改完成事件。所以当你收到文件删除事件(beforeFileDeletion)时,文件已经被删除了。但是,该文件依然存在快照中,你可以使用VFS API从快照中获取文件的最后的内容。
请注意,刷新操作只针对快照加载过的文件触发的事件。例如:如果你加载一个文件夹为 VirtualFile,但是没有使用 VirtualFile.getChildren() 方法加载它的子文件,如果在该文件夹下面创建了某个文件,你可能收不到该文件创建( fileCreated )事件。
如果你使用了 VirtualFile.findChild() 加载了文件夹下面的某一个文件,你会收到该文件的所有修改事件。但是你收不到同一个文件夹下面的其它事件,例如 created,deleted等事件