如何知道 File Observer 监视的已删除文件是目录还是文件

How to know if removed File monitored by File Observer is a Directory or a File

我正在使用 FileObserver 来监视文件夹中的更改。

事件按预期触发,但我无法区分事件 DELETEMOVED_FROM 中的文件和目录,因为在触发事件后,调用两者 File.isFile() File.isDirectory() 是错误的(这是有道理的)。

有没有一种有效的方法可以在删除文件之前进行此检查?我确实有一个解决方法,可以列出受影响文件夹中的所有文件,但效率很低。

文件观察者代码:

 mFileObserver = new FileObserver(DIRECTORY.getPath()) {
            @Override
            public void onEvent(int event, String path) {
                event &= FileObserver.ALL_EVENTS;
                switch (event) {
                    case (CREATE):
                    case (MOVED_TO):
                        Log.d(TAG, "Added to folder: " + DIRECTORY + " --> File name " + path);
                        addChild(path);
                        break;
                    case (DELETE):
                    case (MOVED_FROM):
                        Log.d(TAG, "Removed from folder " + DIRECTORY + " --> File name " + path);                            
                        removeChild(path);
                        break;
                    case (MOVE_SELF):
                    case (DELETE_SELF):
                        removeDirectory();
                        break;
                }
            }               
        };

编辑:

这就是 File/Folder 在 removeChild(String)

中的计算方式
private void removeChild(String name) {

        mFileObserver.stopWatching();

        String filepath = this.getAbsolutePath() + separator + name;
        File file = new File(filepath);
        if (file.exists())
            Log.d(TAG, "Exists");
        else Log.d(TAG, " Does not Exists");

        if (file.isDirectory())
            Log.d(TAG, "is Directory");
        else Log.d(TAG, " is  NOT Directory");

        if (file.isFile())
            Log.d(TAG, "is File");
        else Log.d(TAG, " is  NOT File");
    }

相关的 logcat 输出是:

04-03 12:37:20.714 5819-6352:  Removed from folder /storage/emulated/0/Pictures/GR --> File name ic_alarm_white_24dp.png
04-03 12:37:20.714 5819-6352:  Does not Exists
04-03 12:37:20.714 5819-6352:  is  NOT Directory
04-03 12:37:20.714 5819-6352:  is  NOT File

Is there an efficient way to make this check before file is removed?

不幸的是,我不知道。这是有道理的 - 文件系统 事件 是已经发生的事情。

FileObserver uses inotify to get events. A good description of inotify functionality can be found at https://www.ibm.com/developerworks/library/l-inotify/:

Monitor Linux file system events with inotify

...

Before inotify there was dnotify. Unfortunately, dnotify had limitations that left users hoping for something better. Some of the advantages of inotify are:

  • Inotify uses a single file descriptor, while dnotify requires opening one file descriptor for each directory that you intend to watch for changes. This can be very costly when you are monitoring several directories at once, and you may even reach a per-process file descriptor limit.
  • The file descriptor used by inotify is obtained using a system call and does not have an associated device or file. With dnotify, the file descriptor pins the directory, preventing the backing device to be unmounted, a particular problem with removable media. With inotify, a watched file or directory on a file system that is unmounted generates an event, and the watch is automatically removed.
  • Inotify can watch files or directories. Dnotify monitors directories, and so programmers had to keep stat structures or an equivalent data structure reflecting the files in the directories being watched, then compare those with the current state after an event occurred in order to know what happened to the entry in the directory.
  • As noted above, inotify uses a file descriptor, allowing programmers to use standard select or poll functions to watch for events. This allows for efficient multiplexed I/O or integration with Glib's mainloop. In contrast, dnotify uses signals, which programmers often find more difficult or less than elegant. Signal-drive I.O notification was also added to inotify in kernel 2.6.25.

The API for inotify

...

请注意,没有提及 "events about to happen" 或类似内容。

您不需要保留所有文件的列表 - 您只需要一个目录列表 - 一个简单的 Set<String> 就可以了。如果删除的String path在集合中,就是一个目录。

为了更稳健的方法,当您启动手表时,您还可以在您主要查看的目录中的所有目录上放置一个 FileObserver 手表(也可以在您的主要目录中创建的每个目录中添加一个手表创建观察者后的目录)。

然后,如果您从子 FileObserver 对象之一得到一个 DELETE_SELF,您就会知道它是一个目录。如果事件没有获得 DELETE_SELF 事件的关联子 FileObserver 对象,则它不是目录。

对于一个非常大的目录,这种方法诚然会存在可伸缩性问题...