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
- MergeCursor引起的CursorAdapter更新无效问题
- CursorAdapter的Cursor关闭问题
- spring+mybatis使用MapperScannerConfigurer引起的PropertyPlaceholderConfigurer无效问题
- weblogic更新文件无效的问题
- Django中更新数据库无效的问题
- 关于在Java中使用request.getRequestDispatcher(".....")引起的css文件无效或图片无效问题。
- CursorAdapter 更新下载进度
- GitHub 一个小问题引起的无法更新
- lua文件热更新无效和资源热更新无效的问题
- 用正则表达式定位引起ORA-01722: 无效数字的问题数据
- <javascript>this不同调用模式引起的问题,函数内部调用函数this无效
- spring+mybatis使用MapperScannerConfigurer引起的PropertyPlaceholderConfigurer无效问题处理方法
- 修复 Ubuntu apt 更新时签名无效的问题
- ubuntu12.04.4更新软件源“GPG签名无效”的问题
- Xcode更新或者重新安装导致插件无效问题的解决方案
- 解决maven jar包 miss 更新无效的问题。
- 修复 Ubuntu apt 更新时签名无效的问题
- 解决maven jar包 miss更新无效的问题
- 题目1439:Least Common Multiple
- 从菜鸟到架构师(十七)
- 一个例子包含mvp、rxjava以及retrofit2的使用
- JS,文本框只能输入整数或两位小数的整数
- 正向代理与反向代理的区别【Nginx读书笔记】
- MergeCursor引起的CursorAdapter更新无效问题
- Problem I: n皇后问题
- 循环 & 递归 & 递推 之间的联系和区别
- Python读写中文注意事项
- mysql--union
- APP保活
- 控制台出现 jQuery 1.9 .live() is not a function 解决方法
- Pandas中ix,loc,iloc有什么区别?
- 题目1440:Goldbach's Conjecture