项目中,用到了AsyncQueryHandler,所以在这里将自己对它的理解写出来,做一个总结。如果有错误的地方,或者理解不到位的地方,请各位高手批评指正~
AsyncQueryHandler是一个抽象类,主要是用来在异步线程中访问数据库,当访问结束后通知界面更新。不过,可千万不要以为AsyncQueryHandler只是用于异步查询数据库哦(虽然这个抽象类名的字面含义是“异步查询”),看过其实现源码就知道,它能做到对数据库进行异步的增删改查。
我们来通过源码看一下,AsyncQueryHandler究竟是如何实现异步操作数据库的功能的。
AsyncQueryHandler是一个抽象类,继承自Handler,它的构造函数是这样的:
public AsyncQueryHandler(ContentResolver cr) {
super();
mResolver = new WeakReference(cr);
synchronized (AsyncQueryHandler.class) {
if (sLooper == null) {
HandlerThread thread = new HandlerThread("AsyncQueryWorker");
thread.start();
sLooper = thread.getLooper();
}
}
mWorkerThreadHandler = createHandler(sLooper);
}
构造函数中,主要是判断当前类中的静态Looper对象是否为null,如果为空,就新建一个HandlerThread,并获得sLooper;如果不为空,就利用这个sLooper,新建一个Handler。由此可知,新建的handler就是在异步线程的。
我们再来看createHandler方法:
protected Handler createHandler(Looper looper) {
return new WorkerHandler(looper);
}
由此可知,AsyncQueryHandler的构造函数,主要做的就是在异步线程中创建了一个WorkHandler对象。那么,这个WorkHandler又是一个什么样的类呢?其实,它就是一个普通的Handler的子类,我们主要来看它的handlerMessage方法:
@Override
public void handleMessage(Message msg) {
final ContentResolver resolver = mResolver.get();
if (resolver == null) return;
WorkerArgs args = (WorkerArgs) msg.obj;
int token = msg.what;
int event = msg.arg1;
switch (event) {
case EVENT_ARG_QUERY:
Cursor cursor;
try {
cursor = resolver.query(args.uri, args.projection,
args.selection, args.selectionArgs,
args.orderBy);
// Calling getCount() causes the cursor window to be filled,
// which will make the first access on the main thread a lot faster.
if (cursor != null) {
cursor.getCount();
}
} catch (Exception e) {
Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
cursor = null;
}
args.result = cursor;
break;
case EVENT_ARG_INSERT:
args.result = resolver.insert(args.uri, args.values);
break;
case EVENT_ARG_UPDATE:
args.result = resolver.update(args.uri, args.values, args.selection,
args.selectionArgs);
break;
case EVENT_ARG_DELETE:
args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
break;
}
// passing the original token value back to the caller
// on top of the event values in arg1.
Message reply = args.handler.obtainMessage(token);
reply.obj = args;
reply.arg1 = msg.arg1;
if (localLOGV) {
Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
+ ", reply.what=" + reply.what);
}
reply.sendToTarget();
}
我们可以看到,AsyncQueryHandler将对数据库的增删改查操作放到了WorkHandler的handlerMessage方法中。也就是说,AsyncQueryHandler将对数据库的操作放在了WorkHandler对象所在的异步线程中了。另外,在handlerMessage方法的最后,又将数据库操作结果封装到Message对象中,通过这个Message对象将操作结果再一次发出去。至于这个Message对象,我们下面结合一系列startXXX方法来说。(XXX代表Query/Insert/Update/Delete)
我们平时在用AsyncQueryHandler的时候,是先继承AsyncQueryHandler类,并实现其onXXXComplete方法。然后在使用的时候,startXXX就可以了。那么这个startXXX又做了什么呢?我们以startQuery方法来说:
public void startQuery(int token, Object cookie,
Uri uri, String[] projection, String selection,
String[] selectionArgs, String orderBy) {
// Use the token as what so cancelOperations works properly
//cancelOperation(token);
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_QUERY;
WorkerArgs args = new WorkerArgs();
args.handler = this;
args.uri = uri;
args.projection = projection;
args.selection = selection;
args.selectionArgs = selectionArgs;
args.orderBy = orderBy;
args.cookie = cookie;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
在startQuery方法中,将该方法的参数都放进了args对象中,而且,要注意到的是,arg.handler=this,也就是说,将AsyncQueryHandler这个类对象本身也放进了args对象中。然后,将args对象放进msg对象中,通过构造函数产生的mWorkerThreadHandler发送出去。根据Handler的基本知识,我们知道,这个msg就被传入了WorkHandler类的handlerMessage方法中,然后就可以根据传进来的参数,执行对应的查询操作啦。现在,我们就可以解释WorkHandler的handlerMessage方法最后所产生的Message对象了。还是看一下WorkHandler中handlerMessage的代码:
... //前面的代码省略
// passing the original token value back to the caller
// on top of the event values in arg1.
Message reply = args.handler.obtainMessage(token);
reply.obj = args;
reply.arg1 = msg.arg1;
if (localLOGV) {
Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
+ ", reply.what=" + reply.what);
}
reply.sendToTarget();
我们可以看到,Message对象就是通过startQuery中传入的handler得到的,也就是AsyncQueryHandler对象本身,那么,这个消息最后通过sendToTarget方法发送出去,去承接这个消息的方法就一定是AsyncQueryHandler的handlerMessage方法了!哈哈,也就是说,这样就能够把异步操作数据库的结果传到主线程来进行处理了~
我们来看一下AsyncQueryHandler的handlerMessage方法:
@Override
public void handleMessage(Message msg) {
WorkerArgs args = (WorkerArgs) msg.obj;
if (localLOGV) {
LogUtil.i(TAG, "QueryHandler.handleMessage: msg.what=" + msg.what
+ ", msg.arg1=" + msg.arg1);
}
int token = msg.what;
int event = msg.arg1;
// pass token back to caller on each callback.
switch (event) {
case EVENT_ARG_QUERY:
onQueryComplete(token, args.cookie, (Cursor) args.result);
break;
case EVENT_ARG_BULK_INSERT:
onBulkInsertComplete(token, args.cookie, (Integer) args.result);
break;
case EVENT_ARG_INSERT:
onInsertComplete(token, args.cookie, (Uri) args.result);
break;
case EVENT_ARG_UPDATE:
onUpdateComplete(token, args.cookie, (Integer) args.result);
break;
case EVENT_ARG_DELETE:
onDeleteComplete(token, args.cookie, (Integer) args.result);
break;
}
}
在AsyncQueryHandler的handlerMessage中,调用onQueryComplete方法,就可以完成界面的一些更新以及其它不耗时操作啦。
总的来说,AsyncQueryHandler就是利用分别在主线程和异步线程中的两个handler来实现异步访问数据库的功能的。为了更好地理解,我把它的工作过程归纳为下图:
很简单,就两步:
- 继承AsyncQueryHandler类,并实现onXXXComplete方法。
onXXXComplete方法中主要是处理数据库操作结果,用以完成界面更新。如果不处理结果的话,也可以不实现onXXXComplete方法。
- 利用AsyncQueryHandler实例直接调用startxxx()方法。
在使用startQuery()方法时,各个参数的含义需要说明一下(因为我第一次用的时候就很迷惑。。。):
token:一个令牌,像是”虎符”的作用吧,同一个AsyncQueryHandler类对象中,startQuery()和onQueryComplete()方法的token应该是一致的。
cookie: 这个是在startQuery()中传入,想在onQueryComplete()中使用的对象。没有的话传递null即可。
uri: 这个是想要操作的数据库对应的uri
projection: 想要查询的列
selection: 限制条件
selectionArgs: 限制条件的具体值
orderBy: 排序条件
另外,在实际开发过程中,我们不仅可以将AsyncQueryHandler拿来直接用,还可以根据自己的需要,写一个类似的抽象类,从而让传递的参数更灵活,实现更多的代码需求。