ContentObserver 是一个没有抽象方法的抽象类。它的两个 onChange() 方法是在没有任何逻辑的情况下实现的。而且由于每当发生更改时都会调用它们,因此您必须覆盖它们。
由于 Google 在 API 级别 16 中添加了两个重载的 onChange() 方法之一,因此该方法的默认行为是调用另一个较旧的方法。
下面是正常实现的样子:
class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
// do s.th.
// depending on the handler you might be on the UI
// thread, so be cautious!
}
}
上面的代码有些事情很重要。您必须知道的第一件事是,第二种方法只能从 API 级别 16 开始使用。这就是我添加 SuppressLint 注释的原因。该代码在旧设备上运行良好,但在这种情况下,Android 显然总是调用旧设备。因此,您的代码不应依赖 URI 才能正常工作。
还要注意构造函数中的 Handler 参数。此处理程序用于传递 onChange() 方法。所以如果你在 UI 线程上创建了 Handler,onChange() 方法也会在 UI 线程上被调用。在这种情况下,请避免在此方法中查询 ContentProvider。而是使用 AsyncTask 或 Loader。
如果您将空值传递给构造函数,Android 会立即调用 onChange() 方法——无论当前使用的线程如何。我认为在创建 ContentObserver 对象时最好始终使用处理程序。
注册您的内容观察者以监听变化
要注册您的 ContentObserver 子类,您只需调用 ContentResolver 的 registerContentObserver() 方法:
getContentResolver().registerContentObserver(SOME_URI,true,yourObserver);
它需要三个参数。第一个是要监听的 URI。我将在下一节中更详细地介绍 URI。
第二个参数指示对以给定 URI 开头的 URI 的所有更改是否应触发方法调用,或者仅更改为仅此一个 URI。这对于说带有许多后代的 ContactsContract URI 来说很方便。但它也可能是有害的,因为导致方法调用的实际更改对您来说更加模糊。
第三个参数是您的 ContentObserver 实现的实例。
你可以观察到的 URIs
正如我对内容提供者的介绍中所述,内容 URI 可以是基于目录或基于 id 的。
这两种 URI 类型都可以用于您的内容观察者。如果您有一个详细信息屏幕,您将为您的观察者使用基于 id 的 URI,而当您使用数据列表时,基于目录的 URI 更合适。
但这并不总是有效。例如,ContactsContract 总是会触发更改,无论何时更改任何联系人,即使您正在侦听更具体的 URI。这取决于内容提供者的正确实现。我已经为 ContactsContract 提供者提交了错误报告。如果您同意,请为这个问题投票。
当您为应用编写内容提供程序时,请注意通知正确的 URI。仅当您这样做时,此处描述的反馈机制才有效。这对您的观察者很重要——或者如果提供者也为您客户的观察者导出。这对装载机也很重要。请参阅我的帖子,了解如何编写内容提供程序以了解更多信息。
注意:如果您使用 Loaders,您不需要自己监听更改。在这种情况下,Android 会注册一个 ContentObserver 并触发您的 LoaderCallbacks onLoadFinished() 方法进行任何更改。
不要忘记注销您的内容观察者
注册内容观察者后,您也有责任取消注册。否则你会造成内存泄漏,你的 Activity 永远不会被垃圾回收。
要取消注册,请调用 ContentResolver 的 unregisterContentObserver() 方法:
getContentResolver().
unregisterContentObserver(yourObserver);