5

在我的应用程序中,我有不同的组件,它们使用 FileObservers 监视 sdcard 中的特定文件。因此,有两个 File Observer 实例可以观察单个文件,例如所有事件的 abc.xml。

FileObserver fo1 = new FileObserver(new File("/sdcard/abc.xml"));
fo1.startWatching();
FileObserver fo2 = new FileObserver(new File("/sdcard/abc.xml"));
fo2.startWatching();

他们都注册了不同的事件。我的问题是当两个文件观察者同时观看时,我错过了对“fo1”的 onEvent() 的调用。

这是安卓系统的限制吗?有什么方法可以克服这个问题?

4

2 回答 2

9

迟到但可能对其他人有帮助:这是 Android 中的一个错误 -此处报告了该问题。

由于这让我大吃一惊,我写了一个替代 FileObserver 的插件,它通过维护一个 FileObservers 的主列表来解决这个问题。用此 FixedFileObserver 替换应用程序中的所有 FileObserver 应该会产生预期的行为。(健康警告:我没有在所有极端情况下对其进行非常广泛的测试,但它对我有用)

FixedFileObserver.java

package com.fimagena.filepicker.backend;

import android.os.FileObserver;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;


public abstract class FixedFileObserver {

    private final static HashMap<File, Set<FixedFileObserver>> sObserverLists = new HashMap<>();

    private FileObserver mObserver;
    private final File mRootPath;
    private final int mMask;

    public FixedFileObserver(String path) {this(path, FileObserver.ALL_EVENTS);}
    public FixedFileObserver(String path, int mask) {
        mRootPath = new File(path);
        mMask = mask;
    }

    public abstract void onEvent(int event, String path);

    public void startWatching() {
        synchronized (sObserverLists) {
            if (!sObserverLists.containsKey(mRootPath)) sObserverLists.put(mRootPath, new HashSet<FixedFileObserver>());

            final Set<FixedFileObserver> fixedObservers = sObserverLists.get(mRootPath);

            mObserver = fixedObservers.size() > 0 ? fixedObservers.iterator().next().mObserver : new FileObserver(mRootPath.getPath()) {
                @Override public void onEvent(int event, String path) {
                    for (FixedFileObserver fixedObserver : fixedObservers)
                        if ((event & fixedObserver.mMask) != 0) fixedObserver.onEvent(event, path);
                }};
            mObserver.startWatching();
            fixedObservers.add(this);
        }
    }

    public void stopWatching() {
        synchronized (sObserverLists) {
            Set<FixedFileObserver> fixedObservers = sObserverLists.get(mRootPath);
            if ((fixedObservers == null) || (mObserver == null)) return;

            fixedObservers.remove(this);
            if (fixedObservers.size() == 0) mObserver.stopWatching();

            mObserver = null;
        }
    }

    protected void finalize() {stopWatching();}
}
于 2015-09-25T22:52:59.637 回答
0

@Fimagena 的回答非常适合我。对于那些已经转向 Kotlin 并发现由 Java->Kotlin 代码转换器生成的版本无法正常工作的人,这里是一个工作的 Kotlin 版本:

package <your package>

import android.os.FileObserver
import java.io.File

var sObserverLists = mutableMapOf<File, MutableSet<FixedFileObserver>>()

abstract class FixedFileObserver(
        path: String,
        private val eventMask: Int = FileObserver.ALL_EVENTS
) {
    private var fileObserver: FileObserver? = null
    private val rootPath: File = File(path)

    abstract fun onEvent(event: Int, relativePath: String?)

    fun startWatching() {
        synchronized(sObserverLists) {
            if (!sObserverLists.containsKey(rootPath)) {
                sObserverLists[rootPath] = mutableSetOf()
            }

            val fixedObservers = sObserverLists[rootPath]

            fileObserver = if (fixedObservers!!.isNotEmpty()) {
                fixedObservers.iterator().next().fileObserver
            } else {
                object : FileObserver(rootPath.path) {
                    override fun onEvent(event: Int, path: String?) {
                        for (fixedObserver in fixedObservers) {
                            if (event and fixedObserver.eventMask != 0) {
                                fixedObserver.onEvent(event, path)
                            }
                        }
                    }
                }
            }
            fixedObservers.add(this)
            fileObserver!!.startWatching()
        }
    }

    fun stopWatching() {
        synchronized(sObserverLists) {
            val fixedObservers = sObserverLists[rootPath]
            if (fixedObservers == null || fileObserver == null) {
                return
            }

            fixedObservers.remove(this)
            if (fixedObservers.isEmpty()) {
                fileObserver!!.stopWatching()
            }

            fileObserver = null
        }
    }
}

并且为 rxJava/rxKotlin 爱好者提供了一个额外的包装类:

class RxFileObserver(
        private val path: String, eventMask: 
        Int = FileObserver.ALL_EVENTS
) : FixedFileObserver(path, eventMask) {

    private val subject = PublishSubject.create<String>().toSerialized()

    val observable: Observable<String> =
            subject.doOnSubscribe { startWatching() }
                    .doOnDispose { stopWatching() }
                    .share()

    override fun onEvent(event: Int, relativePath: String?) {
        subject.onNext(path)
    }
}
于 2018-08-13T06:40:38.833 回答