乐趣区

关于android:FileObserver失效接收不到event

如何应用 FileObserver

  1. 申请权限
    别忘了在代码中申请动静权限

     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  2. 实现 FileObserver
    实现 FileObserver 时能够指定一个或多个文件及目录,还能够指定关怀的 events。
   private val observer = object : FileObserver(dir.absolutePath, ALL_EVENTS) {override fun onEvent(event: Int, path: String?) {if (debug) {Log.d("FileObserverManager", "onEvent event $event path $path")
                }
            }
        }
  1. 启动监听和进行监听

        init {observer.startWatching()
            if (debug)
                Log.d("FileObserverManager", "init startWatching")
        }

        fun tryRelease() {observer.stopWatching()
            if (debug)
                Log.d("FileObserverManager", "tryRelease stopWatching")
        }

遇到的问题

整个应用过程还是比较简单的,没有什么简单的过程,然而在应用过程中发现有的时候接管不到事件。在应用的过程中有多个画面都须要对门路文件变动进行监听,在画面退出的时候勾销以后画面的监听。在网上搜寻对于 FileObserver 接管不到 event 的问题,大家都是通过全局持有 FileObserver 的形式解决了问题,可是我的问题始终没有解决。
为了解决问题咱们只能看 FileObserver 源代码来剖析问题的起因了。通过 FileObserver 的源代码的确发现了有蹊跷的中央。

   private static ObserverThread s_observerThread;

    static {s_observerThread = new ObserverThread();
        s_observerThread.start();}

这个动态的 ObserverThread 应该就是问题的要害,因为他是动态的,所以所有的 FileObserver 都应用同一个 ObserverThread 来监听文件变动的。

 public void startWatching() {if (mDescriptors == null) {mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
        }
    }

FileObsever 的 startWatching 办法用于开始监听,开始监听后返回数据给 mDescriptors,能够设想咱们能够通过 mDescriptors 找到文件监听的对应关系。上面看下进行监听的代码:

    public void stopWatching() {if (mDescriptors != null) {s_observerThread.stopWatching(mDescriptors);
            mDescriptors = null;
        }
    }

咱们看到进行文件监听时把 mDescriptors 传给 ObserverThread 了,所以认为 mDescriptors 是放弃着文件监听的映射关系应该是没问题了。

我在应用过程中多个界面都会通过 FileObserver 启动监听和进行监听,那么会不会是前面的画面退出后进行了后面画面对雷同的门路的监听。通过重复测试发现的确就是雷同门路的不同 FileObserver 的启动与敞开会相互影响。

如何解决问题

既然每个目录的不同的 FileObserver 会相互影响,那么咱们为每个目录只保护一个 FileObserver 不就能够了吗。

object FileObserverManager {
    private const val debug = false
    private val fileObserverWrappers = hashMapOf<String, FileObserverWrapper>()

    fun register(listener: FileEventListener) {var fileObserverWrapper = fileObserverWrappers[listener.dir.absolutePath]
        if (fileObserverWrapper == null) {fileObserverWrapper = FileObserverWrapper(listener.dir).apply {fileObserverWrappers[listener.dir.absolutePath] = this
            }
        }
        fileObserverWrapper.addFileEventListener(listener)
    }

    fun unregister(listener: FileEventListener) {val fileObserverWrapper = fileObserverWrappers[listener.dir.absolutePath]
        fileObserverWrapper?.removeFileEventListener(listener)
        if (fileObserverWrapper?.tryRelease() == true) {fileObserverWrappers.remove(listener.dir.absolutePath)
        }
    }

    private class FileObserverWrapper(dir: File) {private val fileEventListeners = mutableListOf<FileEventListener>()

        private val observer = object : FileObserver(dir.absolutePath, ALL_EVENTS) {override fun onEvent(event: Int, path: String?) {if (debug) {Log.d("FileObserverManager", "onEvent event $event path $path")
                }
                fileEventListeners.forEach { fileEventListener ->
                    if (event.and(fileEventListener.eventMask) != 0) {fileEventListener.onEvent(event, path)
                    }
                }
            }
        }

        init {observer.startWatching()
            if (debug)
                Log.d("FileObserverManager", "init startWatching")
        }

        fun addFileEventListener(listener: FileEventListener) {fileEventListeners.add(listener)
            if (debug)
                Log.d("FileObserverManager", "addFileEventListener ${fileEventListeners.size}")
        }

        fun removeFileEventListener(listener: FileEventListener) {fileEventListeners.remove(listener)
            if (debug)
                Log.d("FileObserverManager", "removeFileEventListener ${fileEventListeners.size}")
        }

        fun tryRelease(): Boolean {if (fileEventListeners.isEmpty()) {observer.stopWatching()
                if (debug)
                    Log.d("FileObserverManager", "tryRelease stopWatching")
                return true
            }
            return false
        }
    }

    interface FileEventListener {
        val dir: File
        val eventMask: Int
        fun onEvent(event: Int, path: String?)
    }
}

fileObserverWrappers 字段保护了目录与 FileObserver 的映射关系,FileObserverWrapper 中除了治理 FileObserver,它还治理着对雷同目录的不同监听。

fun register(listener: FileEventListener) {var fileObserverWrapper = fileObserverWrappers[listener.dir.absolutePath]
        if (fileObserverWrapper == null) {fileObserverWrapper = FileObserverWrapper(listener.dir).apply {fileObserverWrappers[listener.dir.absolutePath] = this
            }
        }
        fileObserverWrapper.addFileEventListener(listener)
    }

    fun unregister(listener: FileEventListener) {val fileObserverWrapper = fileObserverWrappers[listener.dir.absolutePath]
        fileObserverWrapper?.removeFileEventListener(listener)
        if (fileObserverWrapper?.tryRelease() == true) {fileObserverWrappers.remove(listener.dir.absolutePath)
        }
    }

这两个办法实现了注册门路监听和删除门路监听,同时 FileObserverManager 是单例的,他保护了利用的所有监听。

    interface FileEventListener {
        val dir: File
        val eventMask: Int
        fun onEvent(event: Int, path: String?)
    }

FileEventListener 接口定义了监听的门路和关怀的事件。上面的代码展现了如何定义 listener 进行监听。

val fileEventListener = object : FileObserverManager.FileEventListener {
            override val dir: File
                get() = File("mnt/sdcard")
            override val eventMask: Int
                get() = FileObserver.CREATE.or(FileObserver.DELETE)

            override fun onEvent(event: Int, path: String?) {
                path ?: return
                Log.d("FileObserverManager", "onEvent $path")
            }
        }

fileEventListener 监听的目录是 ”mnt/sdcard”, 监听的事件是创立和删除。

总结

  1. 文件权限必须申请。
  2. ObserverThread 是动态的变量保留的,所以在雷同过程中对雷同门路的不同 FileObserver 的启动与勾销会相互影响。
  3. 应用 FileObserverManager 单例对象保护雷同门路的 FileObserver,保障雷同门路 FileObserver 的唯一性,这样也解决了抵触问题。
退出移动版