AsyncQueryHandler原理探究

##什么是AsyncQueryHandler?
AsyncQueryHandler是一个异步查询操作的帮助类,除了query,它同样可以实现insert,update,delete操作,在其API中提供了下面四个方法,对数据库进行操作

  • startInsert
  • startDelete
  • startUpdate
  • startQuery

并且提供了对应的on××××Complete方法去实现操作完数据库后再进行其他的操作,这四个on××××Complete都是空实现,需要我们根据具体的业务需求去具体的实现.


##使用AsyncQueryHandler有什么好处?
实际项目里我们当然可以使用ContentProvider去操作数据库,这在数据量比较小的时候是没有问题的,但是如果数据量比较大,加载数据比较多,就会是一个非常耗时的操作,就有可能是UI线程导致ANR(Application Not Responding)异常.

当然也可以通过开线程,创建Handler的方式去做这些操作,但是每次使用ContentProvider都要写个Handler,必然会导致效率的下降.

基于上面的考虑,Android官方的API就提供了一个AsyncQueryHandler类来对数据库进行操作.


##AsyncQueryHandler工作流程
下面这个AsyncQueryHandler简化了API里的AsyncQueryHandler,更加直观地展示了它的工作流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public abstract class AsyncQueryHandler extends Handler {
protected class WorkerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// 运行在工作者线程中
WorkerArgs args = (WorkerArgs) msg.obj;
// 向调用者传回信息
Message reply = args.handler.obtainMessage(token);
}
}
public AsyncQueryHandler(ContentResolver cr) {
synchronized (AsyncQueryHandler.class) {
// 启动工作者线程
if (sLooper == null) {
HandlerThread thread = new HandlerThread("AsyncQueryWorker");
thread.start();
sLooper = thread.getLooper();
}
}
// 与工作者线程绑定的Hanlder
mWorkerThreadHandler = createHandler(sLooper);
}
public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
WorkerArgs args = new WorkerArgs();
// 保存调用者Handler对象,以便回调
args.handler = this;
msg.obj = args;
// 向工作者线程发出处理请求
mWorkerThreadHandler.sendMessage(msg);
}
// 被子类重写的事件完成回调接口
protected void onQueryComplete(int token, Object cookie, int result) {
super.onQueryComplete(token, cookie, cursor);
CursorAdapter adapter = (CursorAdapter) cookie;
//通知adapter去刷新数据 并且要把cursor数据传递过去
adapter.changeCursor(cursor);
}
@Override
public void handleMessage(Message msg) {
// 运行在调用者线程
switch (event) {
case EVENT_ARG_QUERY:
onQueryComplete(token, args.cookie, (Cursor) args.result);
break;
}
}
}

各方法执行流程演示如下:


基本上可以这样理解:AsyncQueryHandler对线程和消息处理机制进行了封装.

实际上,根据约定,我们在使用AsyncQueryHandler进行查询的时候,要使用as取别名的方式执行多表查询,将需要查到的数据库中的字段拼接为需要传递的投影,将投影作为参数传递给startQuery方法.

在startQuery方法里各参数的含义如下:

  • token 执行方法的一个唯一标示符,为了个onQueryComplete里的token对应
  • cookie 要传递给onQueryComplete的对象
  • uri 查询操作的uri
  • projection 查询投影字段
  • selection 查询条件
  • selectionArgs 查询参数
  • orderBy 是否排序

具体的执行流程,我们可以这样理解:

  1. 实例化AsyncQueryHandler,开启线程获取消息泵(looper)
  2. 创建mWorkHandler
  3. 在startQuery方法里,获取消息,封装参数到消息里,然后给mWorkHandler发送消息处理请求
  4. WorkHandler类中的handleMessage方法处理对应的请求,如增删改查,将处理请求得到的结果封装到一个新的回复消息中,然后调用sendToTarget方法,发送给目标对象,即AsyncQueryHandler
  5. 在AsyncQueryHandler中也有一个handleMessage方法,handleMessage方法处理消息完成后,会调用对应的onQueryComplete方法.

##Handler+Message原理
Handler+Message的原理可以用下面的序列图来演示:

这里必须要注意一点:
Message 里面传递的Runnable对象不是当成一个线程来使用。

1
2
3
private static void handleCallback(Message msg) {
msg.callback.run();
}

顺便提一下,子线程不能更新显示,我们在使用ProgressDialog的时候,看上去是在子线程更新了显示,实际其内部使用的原理还是Handler+Message.