MergeCursor引起的CursorAdapter更新无效问题

来源:互联网 发布:网络用语立场什么意思 编辑:程序博客网 时间:2024/05/22 10:56

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即可。

0 0
原创粉丝点击