如何在 Android 10 中替换 FileObserver?
Posted
技术标签:
【中文标题】如何在 Android 10 中替换 FileObserver?【英文标题】:How to replace FileObserver in Android 10? 【发布时间】:2020-03-06 17:36:35 【问题描述】:android.os.FileObserver
需要java.io.File
才能运行。
但是在 Android 10 中,由于著名的“存储访问框架”,Google 限制了对除您应用的私有目录之外的所有内容的访问。因此,通过java.io.File
访问任何内容都会中断并呈现FileObserver
无用,除非您打算在应用程序的私有目录中使用它。但是,我希望在外部存储的某个目录中发生更改时收到通知。我还想避免定期检查更改。
我尝试使用ContentResolver.registerContentObserver(uri,notifyForDescendants,observer)
,但该方法遇到了一些问题:
Uri
都被接受了
如果Uri
不起作用,它既不会失败也不会通知
我找不到任何文档告诉我哪个 Uri
s 真正有效
我在某种程度上唯一得到的工作是以下方法:
// works, but returns all changes to the external storage
contentResolver.registerContentObserver(MediaStore.Files.getContentUri("external"), true, contentObserver)
不幸的是,这包括所有外部存储,并且仅在发生更改时返回 Media Uri
s - 例如content://media/external/file/67226
。
有没有办法查明Uri
是否指向我的目录?
或者有没有办法让registerContentObserver()
与Uri
一起工作,这样每当文件夹中的某些内容发生变化时我都会收到通知?
我也没有成功尝试与DocumentsFile
和外部存储Uri
s 相关的各种 Uris。
【问题讨论】:
也许可以尝试传递目录 uri 而不是 External Conten Uri。 我应该提到的,但没有用。 @squaresandcircles 你能解决这个问题吗?我正在从事类似的任务并坚持下去。 @squaresandcircles 你解决了这个问题吗?我被卡住了 问题仍然存在。 【参考方案1】:即使尝试使用以下基本构造函数时,我也会不断收到错误 -
No direct method <init>(Ljava/util/List;I)V in class Landroid/os/FileObserver; or its super classes (declaration of 'android.os.FileObserver' appears in /system/framework/framework.jar!classes2.dex)
来自Detect file change using FileObserver on Android的评论:
当我尝试使用构造函数 FileObserver(File) 时,我看到了该消息(或类似的内容)。 deprecated FileObserver(String) 的使用解决了我的问题....原来的 FileObserver 有错误。
完全披露,我使用的是 Xamarin.Android API;但是,我引用的要点和评论者都在使用 Java。无论如何,确实 - 再次尝试使用对应的 String
构造函数,我终于能够制作和使用观察者。磨我的齿轮以使用已弃用的 API,但显然他们至少坚持到并包括 Android 12.0.0_r3... 仍然希望支持的构造函数实际工作。也许这里有一些提交问题的保证。
【讨论】:
对应的 String 构造函数也适用于我。【参考方案2】:我有这个问题的临时解决方案,所以让我们看看这是否有帮助。
我使用DocumentFile
启动了一个无限的while
循环来监视文件的创建和删除(如果要修改文件或重命名文件,则必须执行更多操作)。以下是我的示例:
private static int currentFileIndirectory = 0;
private static final int FILE_CREATED = 0;
private static final int FILE_DELETED = 1;
private static DocumentFile[] onDirectoryChanged(DocumentFile[] documentFiles, int event)
Log.d("FileUtil", "onDirectoryChanged: " + event);
if (event == FILE_CREATED)
else
return documentFiles;
private static boolean didStartWatching = false;
private static void startWatchingDirectory(final DocumentFile directory)
if (!didStartWatching)
didStartWatching = true;
DocumentFile[] documentFiles = directory.listFiles();
if (null != documentFiles && documentFiles.length > 0)
currentFileIndirectory = documentFiles.length;
new Thread(new Runnable()
public void run()
while (true)
DocumentFile[] documentFiles = directory.listFiles();
if (null != documentFiles && documentFiles.length > 0)
if (documentFiles.length != currentFileIndirectory)
if (documentFiles.length > currentFileIndirectory) //file created
DocumentFile[] newFiles = new DocumentFile[documentFiles.length - currentFileIndirectory];
onDirectoryChanged(newFiles, FILE_CREATED);
else //file Deleted
onDirectoryChanged(null, FILE_DELETED);
currentFileIndirectory = documentFiles.length;
).start();
【讨论】:
这是一个糟糕的实现——它引入了一个剧烈的轮询循环,实际上并没有提供任何新文件的onDirectoryChanged
回调,只是一个大小与新文件数量相符的空数组。不妨将documentFiles
参数更改为int
。【参考方案3】:
我找到了一种在 Android 10 上使用 ContentObserver
实现 FileObserver 的方法,但它可能仅适用于媒体文件,因为它适用于媒体内容 uri。
ContentResolver.registerContentObserver()
的uri应该是文件对应的媒体uri(如content://media/external/file/49
),通过文件路径查询。
fun getMediaUri(context: Context, file: File): Uri?
val externalUri = MediaStore.Files.getContentUri("external")
context.contentResolver.query(
externalUri,
null,
"$MediaStore.Files.FileColumns.DATA = ?",
arrayOf(file.path),
null
)?.use cursor ->
if (cursor.moveToFirst())
val idIndex = cursor.getColumnIndex("_id")
val id = cursor.getLong(idIndex)
return Uri.withAppendedPath(externalUri, "$id")
return null
然后ContentObserver.onChange()
将被uri: content://media/external/file/id
的每个文件更改触发; uri
在ContentObserver.onChange(boolean selfChange, Uri uri)
中将永远是content://media/external/file
;只有注册的文件才会有 id(例如content://media/external/file/49?deletedata=false
)。
当输入 uri 的路径与注册的 uri 匹配时,FileObserver
会做什么。
【讨论】:
以上是关于如何在 Android 10 中替换 FileObserver?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 清单应用程序中添加多个“工具:替换”?
如何在android studio中用另一个片段替换一个片段