如何应用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的唯一性,这样也解决了抵触问题。