CursorAdapter中有个onContentChanged方法,是protected,只能被子类重写。
protected void onContentChanged() {
if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
mDataValid = mCursor.requery();
}
}
在这个方法可以看成是数据变化的通知。最近遇到了一个问题是该方法无效了。
onContentChanged回调的流程
构造方法
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
构造方法中auroRequery为false的情况下,会开启数据监听
void init(Context context, Cursor c, int flags) {
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) { //这里其实看出只要flags>0就会启动监听
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
mAutoRequery = true;
} else {
mAutoRequery = false;
}
...
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
mChangeObserver = new ChangeObserver(); //新建观察者对象
mDataSetObserver = new MyDataSetObserver();
} else {
mChangeObserver = null;
mDataSetObserver = null;
}
if (cursorPresent) {
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver); //注册监听
if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
}
}
观察者
private class ChangeObserver extends ContentObserver {
public ChangeObserver() {
super(new Handler());
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) { //就是之前提及的方法
onContentChanged();
}
}
private class MyDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
mDataValid = true;
notifyDataSetChanged(); //通知数据变化,这个方法会引起UI的重绘
}
@Override
public void onInvalidated() {
mDataValid = false;
notifyDataSetInvalidated(); //通知数据失效
}
}
changeCursor
除了构造方法外,设置cursor的方法也和监听有关:
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
...
}
public Cursor swapCursor(Cursor newCursor) {
...
Cursor oldCursor = mCursor;
if (oldCursor != null) {
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver); //卸载旧cursor的监听
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (newCursor != null) {
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver); //开启新的监听
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
}
...
return oldCursor;
}
registerContentObserver实现和通知
frameworks/base/core/java/android/database/AbstractCursor.java
AbstractCursor是Cursor的实现类,其中两个注册方法如下:
@Override
public void registerContentObserver(ContentObserver observer) {
mContentObservable.registerObserver(observer);
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
通知方法:
protected void onChange(boolean selfChange) {
synchronized (mSelfObserverLock) {
mContentObservable.dispatchChange(selfChange, null);
if (mNotifyUri != null && selfChange) {
mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
}
}
}
public boolean requery() { //也即CursorAdapter的onContentChanged默认实现才会引起DateSetObservable的回调
if (mSelfObserver != null && mSelfObserverRegistered == false) {
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
mSelfObserverRegistered = true;
}
mDataSetObservable.notifyChanged();
return true;
}
通知最终的原理
AbstractCursor内部有个静态类:
protected static class SelfContentObserver extends ContentObserver {
...
@Override
public void onChange(boolean selfChange) {
AbstractCursor cursor = mCursor.get();
if (cursor != null) {
cursor.onChange(false);
}
}
}
public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) {
synchronized (mSelfObserverLock) {
mNotifyUri = notifyUri;
mContentResolver = cr;
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
mSelfObserver = new SelfContentObserver(this);
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle);
mSelfObserverRegistered = true;
}
}
终于找到了监听uri的常规代码,至于ContentResolver怎么监听uri后续的流程不再深究了,网上已有,罗升阳的博客里应该是有分析的。理解为ContactsProvider相关Uri有notify的话,观察者就会收到通知。
bug原因
代码分析可以看出CursorAdapter整个流程要跑通,setNotificationUri是必须要调用的。
参见ContactsProvider2中的一段代码,
private Cursor doQuery(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
String selection, String[] selectionArgs, String sortOrder, String groupBy,
String having, String limit, CancellationSignal cancellationSignal) {
...
final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having,
sortOrder, limit, cancellationSignal);
if (c != null) {
LogUtils.d(TAG, "[query]c.count(): " + c.getCount());
c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
}
return c;
}
因为加入了setNotificationUri,CursorAdapter的回调才会起作用。
可见自定义ContentProvider中是要加入setNotificationUri的代码的,如果忘记就不起效果了。不过我遇到的bug中,并不是ContentProvider中没有调用setNotificationUri。而是代码中使用了MergeCursor,合并了两个Cursor对象(但是这两个对象没有调用setNotificationUri),并且MergeCursor也调用了setNotificationUri。
注意MergeCursor看做Cursor的一个容器比较恰当,看做cursor的一个子类就不太符合子类的行为。
public void registerContentObserver(ContentObserver observer) {
int length = mCursors.length;
for (int i = 0 ; i < length ; i++) {
if (mCursors[i] != null) {
mCursors[i].registerContentObserver(observer);
}
}
}
其中的registerContentObserver实现就是调用mCursors数组中的每个对象的registerContentObserver方法,自身并没有实际注册监听。所以只有
mCursors中的对象有调用setNotificationUri,MergeCursor才会触发回调,只有MergeCursor自己调用setNotificationUri是没有效果的。
如果mCursors数组中的对象同属一个监听uri,会引发回调的多次触发,所以这种情况下只要一个对象调用setNotificationUri即可。