最近在项目中涉及到了即时聊天,因此不可避免地要用到实时刷新的功能,因为在以前的项目中见到别人使用CursorLoader+CursorAdapter+ContentProvider的机制来实现实时刷新,于是没有多研究就直接照搬了这个机制,直到后来出现了发送消息后不能更新到界面上的问题,查了很久也查不出原因,于是就想从这个机制本身出发,看看有可能是在哪个环节出了问题。
由于我们的数据存储在数据库中,因此这里的泛型应该替换为Cursor
这个接口中有三个方法:
// 这个方法在初始化Loader时回调,我们要在这个方法中实例化CursorLoader
public Loader
// 加载数据完成后回调到这个方法,我们一般在这里调用CursorAdapter的changeCursor或swapCursor进行界面刷新的操作
public void onLoadFinished(Loader
// 这个方法是在重启Loader时调用,一般可以不管
public void onLoaderReset(Loader
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
synchronized (DBLOCK) {
SQLiteDatabase db = WeChatDBManager.getInstance(getContext()).getDatabase();
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
Cursor cursor = null;
switch (sUriMatcher.match(uri)) {
case CODE_CHAT_HISTORY:
queryBuilder.setDistinct(false);
queryBuilder.setTables(uri.getQuery());
cursor = queryBuilder.query(db,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
// 对查询到的结果集对应的Uri设置观察者
if (cursor != null)
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
…
// 通知对应的Uri数据发生改变
getContext().getContentResolver().notifyChange(uri, null);
}
@Override
public int delete(Uri uri, String selection, String\[\] selectionArgs) {
...
// 通知对应的Uri数据发生改变
getContext().getContentResolver().notifyChange(uri, null);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String\[\] selectionArgs) {
...
// 通知对应的Uri数据发生改变
getContext().getContentResolver().notifyChange(uri, null);
}
public
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
LoaderInfo info = mLoaders.get(id);
if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
if (info == null) {
// 创建Loader
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, " Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
if (info.mHaveData && mStarted) {
// Loader中已经有数据,这里最终会回调到onLoadFinished方法
info.callOnLoadFinished(info.mLoader, info.mData);
}
return (Loader<D>)info.mLoader
}
这里主要关注createAndInstallLoader方法
private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks
首先来看看createLoader
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks
在onCreateLoader中我们创建了具体的Loader,即CursorLoader
@Override
public Loader
return new CursorLoader(…);
}
接着执行到installLoader
void installLoader(LoaderInfo info) {
// 把上一步创建的LoaderInfo对象存到列表中
mLoaders.put(info.mId, info);
if (mStarted) {
// 启动Loader
info.start();
}
}
void start() {
...
// start方法中我们只关注startLoading方法
mLoader.startLoading();
...
}
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
// onStartLoading是个空方法,我们要看CursorLoader中的具体实现
onStartLoading();
}
@Override
protected void onStartLoading() {
// 更新数据
if (mCursor != null) {
deliverResult(mCursor);
}
// 初始化时调用 主要看这里,这里又调到父类Loader中的forceLoad
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
public void forceLoad() {
// 这里的onForceLoad又是一个空方法,调用的是子类AsyncTaskLoader中的onForceLoad
onForceLoad();
}
@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
// 这里执行了一个异步任务,接下来看看这个异步任务具体做了什么事
mTask = new LoadTask();
if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
executePendingTask();
}
接下来具体看一下这个异步任务,具体关注其中的doInBackground和onPostExecute
@Override
protected D doInBackground(Void… params) {
if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
try {
// 这里执行了onLoadInBackground
D data = AsyncTaskLoader.this.onLoadInBackground();
if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
return data;
} catch (OperationCanceledException ex) {
if (!isCancelled()) {
throw ex;
}
if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
return null;
}
}
protected D onLoadInBackground() {
// 这里调用到CursorLoader的loadInBackground
return loadInBackground();
}
@Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
// 这里调用ContentResolver进行查询,查询条件就是前面我们在onCreateLoader创建CursorLoader对象时
// 传入的,这里最终会调用我们的ContentProvider,我们在ContentProvider的query中对Cursor对象设置了监听的Uri
Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
mCancellationSignal);
if (cursor != null) {
try {
// 这里给Cursor对象注册了一个内容观察者,而在上面我们设置了要监听的Uri,因此当数据变化时,首先会通知Cursor,然后Cursor再触发ForceLoadContentObserver中的onChange
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
onContentChanged();
}
}
public void onContentChanged() {
if (mStarted) {
// 这里又回到了forceLoad方法,接下来就是重复一遍上面的流程
forceLoad();
} else {
mContentChanged = true;
}
}
异步任务最后会执行onPostExecute
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}
void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) {
if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
if (DEBUG) Log.v(TAG, "Delivering result");
// 传递数据
deliverResult(data);
}
}
}
public void deliverResult(D data) {
if (mListener != null) {
// 回调到LoaderManager中的onLoadComplete
mListener.onLoadComplete(this, data);
}
}
@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
...
// 这里我们只关注callOnLoadFinished,这个方法中最终会回调到我们的onLoadFinished
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
...
}
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
if (mHost != null) {
lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
// 回调到我们的onLoadFinished
mCallbacks.onLoadFinished(loader, data);
} finally {
if (mHost != null) {
mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
}
}
整个流程还是比较清晰的,再梳理一遍:
根据上面的流程我们可以知道,每次数据发生改变时,最后都会触发loadInBackground中的查询,但是这里的查询条件一直是我们在创建CursorLoader时设置的查询条件,而我的项目中涉及到了分页查询(应用场景就是类似手机qq查看历史聊天记录),发生的问题就是当新增的数据达到一定数量时,界面就不会更新了,即如果在查询条件中含有动态改变的limit条件(如分页查询时的页数),就会产生问题。
我的解决方法是每次数据库变化之后都通过CursorLoader的一系列set方法更新查询条件
手机扫一扫
移动阅读更方便
你可能感兴趣的文章