观察Android内容观察器中Audio.Media.EXTERNAL_CONTENT_URI的更改

13

我正在开发一个 Android 应用程序,需要检测 Android SD 卡中音频文件的更改,包括文件名、文件路径以及执行的操作。例如,如果我在 SD 卡中添加了一个文件,则想知道:

  1. 添加的文件名称
  2. 文件路径
  3. 操作类型 -- 添加

之前我尝试使用文件观察器 (file observer),但这需要对每个目录都应用一次。因此,我搜索了其他解决方案,并了解到了Audio.Media.EXTERNAL_CONTENT_URI。然后,我创建了一个内容观察器,代码如下:

UriObserver.java -- 这是一个内容观察器。

class UriObserver extends ContentObserver {

    public UriObserver(Handler handler) {
        super(handler);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onChange(boolean selfChange) {
        // TODO Auto-generated method stub
        super.onChange(selfChange);

        Log.d("INSTANT", "GETTING CHANGES");
    }

}

这是注册的代码

UriObserver observer = new UriObserver(new Handler());

Log.d("INSTANT", "registered content observer");

this.getApplicationContext()
    .getContentResolver()
    .registerContentObserver(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false,
    observer);

Log.d("INSTANT", "registered content observer");

它让我知道在与音频文件有关的SD卡中发生了一些更改。但它没有提供任何关于哪个文件已添加、编辑或删除的信息。

然后我搜索了解决方案并找到了这篇文章:

Android: How to detect a change in MediaStore when connected over MTP

在这篇文章中,Bhiefer给出了一些代码作为答案,我认为它可能有效,因此我尝试实现它,但我做不到。

我该怎么办?

更新

我能查询Audio.Media.EXTERNAL_CONTENT_URI以获取其最新更改吗? 这段代码:

mCursor = context.getContentResolver().query(
                    Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, "_id");

mCursor.moveToLast();

无法获取最新更改。有没有其他方法可以获取最新更改?


你好,我遇到了相同的问题,你找到解决方法了吗? - Nand
你好@Nikhi Agrawal,你能找到内容观察器的解决方案吗?即使是将File Observer递归执行也不能在最新的设备上工作。 - MRamzan
2个回答

10

让我试着解开

ContentObserver

它不会告诉你有什么改变

这是设计上的原因。在文档中没有说它会提供这些信息。

FileObserver

它不是递归的

是的。这是一个已知的问题。遍历所有目录并设置观察器有什么问题呢?据我所知,默认情况下不应该有很多(比如一打左右)。

Android:如何在通过MTP连接时检测MediaStore的更改

你找到的代码只是UriObserver中封装的ContentObserver。

它做了几件事情:

  • 他为其中一个内容提供者(在他的案例中,我相信是来自MediaStore的图像)获取游标

  • 他为此注册了一个观察器

  • 一旦发生更改,就将其转发给外部监听器

然而,这种解决方案有两个限制:

  • 它具有ContentObserver的固有问题,即不报告数据发生了什么变化。

  • 我相信它只会报告在此MediaStore内容提供程序中注册的文件的更改。我相信系统只会扫描SD卡上的特定目录以检查图像等内容。因此,如果一个文件被放置在另一个目录中,这个解决方案就看不到它。

那么,你对他的代码有什么问题吗?

总结

在这种情况下,如果您想知道所有sdcard上文件的确切更改类型,我认为找不到比FileObserver更好的东西了。

更新1

还有几个想法,可能并不适合你。如果你可以root设备,那么你可以编写文件系统的过滤器驱动程序,这样每当有更改发生时就会调用你的驱动程序。

您可以查看此链接: http://www.gossamer-threads.com/lists/linux/kernel/355190

或者您可以重用一些现有的Linux更改通知系统。例如,看看这个: http://stefan.buettcher.org/cs/fschange/。但是,FileObserver可能正是基于它。

无论如何,这两种方法都是低级别的,需要更多时间来弄清楚。


但我必须在所有目录上实现文件观察器。这将是一个漫长的过程。 - Nikhil Agrawal
@Nikhil:这取决于目录数量。如果有数百个目录,那么可能需要很长时间。然而,对于Android SD卡来说,通常没有那么多文件夹。顺便问一下,你试过这样做吗?会让设备变慢吗? - Victor Ronin
是的,我的16 GB SD 卡里有1500多个文件夹。这占用了更多的资源并使我的设备变慢。这就是为什么我正在尝试这个选项。 - Nikhil Agrawal
我授予您+50声望,因为在发布了这么多开发者门户网站后,您引导我解决了我的问题并帮助我进入了下一个级别。但它还没有完成。因此,如果您遇到与此问题相关的任何其他信息,请更新您的答案。对于那些发现这篇文章对他们有用的人,如果您遇到与此问题相关的任何其他信息,请在此文章中发布您的答案。谢谢维克托·罗宁(Victor Ronin),请保持联系。感谢stackoverflow、我们友好和聪明的管理员以及这个社区所有伟大、聪明的成员们。 - Nikhil Agrawal

1
你可以通过查询 DATE_ADDED 和 DATE_MODIFIED 列来获取最新的添加/修改,但是你将无法获取删除的内容
以下是我的做法:
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
long lastDatabaseUpdateTime = preferences.getLong("lastDatabaseUpdateTime", 0);
long newDatabaseUpdateTime = (new Date()).getTime();

String[] cols = new String[] {MediaStore.Audio.Media._ID /* and other columns */};

String where = "("+MediaStore.MediaColumns.DATE_ADDED + ">" + (lastDatabaseUpdateTime/1000);
where += " or " + MediaStore.MediaColumns.DATE_MODIFIED + ">" + (lastDatabaseUpdateTime/1000);
where += ") and "+MediaStore.Audio.AudioColumns.IS_MUSIC+"==1 ";

Cursor cursor = MusicUtils.query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cols, where, null, null);

/* Do my work on the cursor and close the cursor */

//If no exceptions, then save the new timestamp
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("lastDatabaseUpdateTime", newDatabaseUpdateTime);
editor.commit();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接