Android 同一文件上的多个FileObserver失败

Android 同一文件上的多个FileObserver失败,android,android-file,fileobserver,Android,Android File,Fileobserver,在我的应用程序中,我有不同的组件,它们使用FileObserver监视SD卡中的特定文件。因此,有两个File Observer实例可以观察单个文件,比如abc.xml for all events FileObserver fo1 = new FileObserver(new File("/sdcard/abc.xml")); fo1.startWatching(); FileObserver fo2 = new FileObserver(new File("/sdcard/abc.xml")

在我的应用程序中,我有不同的组件,它们使用FileObserver监视SD卡中的特定文件。因此,有两个File Observer实例可以观察单个文件,比如abc.xml for all events

FileObserver fo1 = new FileObserver(new File("/sdcard/abc.xml"));
fo1.startWatching();
FileObserver fo2 = new FileObserver(new File("/sdcard/abc.xml"));
fo2.startWatching();
它们都是为不同的事件注册的。我的问题是,当两个文件观察者同时观察时,我错过了对“fo1”的onEvent()的调用


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

很晚了,但可能对其他人有帮助:这是安卓系统的一个bug——这个问题已经被报道了

因为这让我毛骨悚然,我写了一个替代FileObserver的drop,它通过维护FileObserver的主列表来解决这个问题。使用此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();}
}
package com.fimagena.filepicker.backend;
导入android.os.FileObserver;
导入java.io.File;
导入java.util.HashMap;
导入java.util.HashSet;
导入java.util.Set;
公共抽象类FixedFileObserver{
private final static HashMap sObserverLists=new HashMap();
专用文件服务器;
私人最终文件mRootPath;
私人终审法院;
public FixedFileObserver(字符串路径){this(路径,FileObserver.ALL_事件);}
公共FixedFileObserver(字符串路径,整数掩码){
mRootPath=新文件(路径);
mMask=掩模;
}
公共抽象void onEvent(int事件,字符串路径);
公共无效开始匹配(){
已同步(SobServerList){
如果(!sObserverLists.containsKey(mRootPath))sObserverLists.put(mRootPath,new HashSet());
最终设置fixedObservers=sObserverLists.get(mRootPath);
mObserver=fixedobserver.size()>0?fixedobserver.iterator().next().mObserver:newfileobserver(mRootPath.getPath()){
@重写公共void onEvent(int事件,字符串路径){
对于(FixedFileObserver fixedObserver:fixedObserver)
如果((event&fixedObserver.mMask)!=0)fixedObserver.onEvent(event,path);
}};
mObserver.startWatching();
添加(此);
}
}
公众观望{
已同步(SobServerList){
设置fixedObservers=sObserverLists.get(mRootPath);
if((fixedobserver==null)| |(mObserver==null))返回;
fixedObservers.remove(这个);
如果(fixedObservers.size()==0)mObserver.stopWatching();
mObserver=null;
}
}
受保护的void finalize(){stopWatching();}
}

@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
        }
    }
}
包
导入android.os.FileObserver
导入java.io.xml文件
var sObserverLists=mutableMapOf()
抽象类FixedFileObserver(
路径:字符串,
private val eventMask:Int=FileObserver.ALL_事件
) {
私有变量fileObserver:fileObserver?=null
private val rootPath:File=File(路径)
抽象趣味事件(事件:Int,相对路径:String?)
有趣的打球{
已同步(SobServerList){
如果(!sObserverLists.containsKey(rootPath)){
sObserverLists[rootPath]=mutableSetOf()
}
val fixedObservers=sObserverLists[rootPath]
fileObserver=if(fixedobserver!!.isNotEmpty()){
FixedObserver.iterator().next().fileObserver
}否则{
对象:FileObserver(rootPath.path){
覆盖事件(事件:Int,路径:String?){
用于(fixedObserver中的fixedObserver){
if(event and fixedObserver.eventMask!=0){
fixedObserver.OneEvent(事件,路径)
}
}
}
}
}
添加(此)
fileObserver!!.startWatching()
}
}
看电视的乐趣{
已同步(SobServerList){
val fixedObservers=sObserverLists[rootPath]
if(fixedobserver==null | | fileObserver==null){
返回
}
FixedObserver.remove(此)
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)
    }
}
类RxFileObserver(
私有val路径:字符串,事件掩码:
Int=FileObserver.ALL\u事件
):FixedFileObserver(路径、事件掩码){
private val subject=PublishSubject.create().toSerialized()
可观察的,可观察的=
subject.doOnSubscribe{startWatch()}
.doOnDispose{stopWatching()}
.share()
覆盖有趣的OneEvent(事件:Int,相对路径:String?){
subject.onNext(路径)
}
}

同样的问题。我在活动中有第一名观察员,在服务中有第二名观察员。OneEvent仅在observer上呼叫,最新呼叫StartChingoMG感谢分享您的解决方案!我被这只虫子击中后,差点把头发拔了出来。我试过了,它也适用于我的案例:)很好的解决方案,希望在谷歌在未来的某个版本中进行修复时,它不会中断。当有修复时,它不应该中断——只需增加一点开销。然而,谷歌已经3.5年没有解决这个问题了,所以我不会屏住呼吸。。。