1,AsyncQueryHandler 简介
AsyncQueryHandler的目的就是为了将查询数据库的操作放到后台执行,当后台数据查询完了以后,再通知界面的更新,以此来提高前台页面的显示速度!当然你也可以使用ContentProvider去操作数据库。这在数据量很小的时候是没有问题的,但是如果数据量大了,可能导致UI线程发生ANR事件。当然你也可以写个Handler去做这些操作,只是你每次使用ContentProvider时都要再写个Handler,必然降低了效率。因此API提供了一个操作数据库的通用方法。
AsyncQueryHandler 内部的实现机制其实就是Handler,我们自己平时做大数据量显示的时候也用到过,另起一个线程去执行任务,当后台计算完成的时候通过Handler发送Message来重绘界面,这个的实现原理类似,做了一层封装而已,方便开发人员的调用。
AsyncQueryHandler 继承了Handler对象,但是他提供的构造方法中却没有Looper参数,也就是说他和所在的当前线程绑定。
2,AsyncQueryHandler 使用方式
1)需要继承AsyncQueryHandler类,并提供onxxxComplete方法的实现(可以实现任何一个或多个,当然你也可以一个也不实现,如果你不关注操作数据库的結果),在你的实现中做一些对数据库操作完成的处理。
AsyncQueryHandler handler = new AsyncQueryHandler(this.getContentResolver())
{
@Override
protected void onQueryComplete(int token, Object cookie,Cursor cursor)
{}
@Override
protected void onUpdateComplete(int token, Object cookie, int result)
{}
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri)
{}
@Override
protected void onDeleteComplete(int token, Object cookie, int result)
{}
};
2)使用时直接调用startxxx(或者其他四个中的任何一个)方法即可。传入的通用参数如下:
①token,一个令牌,需要跟onQueryComplete方法传入的一致。(当然你也可以不一致,同样在数据库的操作结束后会调用对应的onQueryComplete方法 )。
②cookie,你想传给onQueryComplete方法使用的一个对象。(没有的话传递null即可)。
③Uri uri(进行查询的通用资源标志符)。
④projection 查询的列
⑤selection 限制条件
⑥orderBy 排序条件
handler.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);
handler.startDelete(token, cookie, uri, selection, selectionArgs);
handler.startInsert(token, cookie, uri, initialValues);
handler.startUpdate(token, cookie, uri, values, selection, selectionArgs);
在使用时每个AsyncQueryHandler对象都会开启一个后台线程,在线程中执行与ContentProvider组件的数据交互,进行增删改查。
调用时,可以通过AsyncQueryHandler.startXXX系列方法将请求打包发送到后台线程。
当相关处理完成后,会将结果异步回传给主线程并回调AsyncQueryHandler.onXXXComplete方法通知调用者。
调用者每次请求时,需要传入一个整型值token作为这次请求的标识,当该请求完成后进行回调时,会将token传回,帮助调用者确定这是哪一次请求。
3,AsyncQueryHandler源码详解
基本策略如下:
1. 当你实例化一个AsyncQueryHandler类时(包括其子类...),它会单件构造一个线程WorkerHandler,这个线程里面会构建一个消息循环。
2. 获得该消息循环的指针,用它做参数实例化另一个Handler类,该类为内部类。至此,就有了两个线程,各自有一个Handler来处理消息。
3. 当调用onXXX的时候,在XXX函数内部会将请求封装成一个内部的参数类,将其作为消息的参数,将此消息发送至内部类线程。
4. 在内部类线程的Handler中,接受该消息,并分析传入的参数,用初始化时传入的ContentResolver进行XXX操作,并返回Cursor或其他返回值。
5. 构造一个消息,将上述返回值以及其他相关内容绑定在该消息上,发送回主线程。
6. 主线程默认的AsyncQueryHandler类的handleMessage方法(可自定义,但由于都是内部类,基本没有意义...)会分析该消息,并转发给对应的onXXXComplete方法。
7. 用户重写的onXXXComplete方法开始工作。
源码分析如下:
private static final int EVENT_ARG_QUERY = 1;
private static final int EVENT_ARG_INSERT = 2;
private static final int EVENT_ARG_UPDATE = 3;
private static final int EVENT_ARG_DELETE = 4;
//ContentResolver由构造器注入:
final WeakReference mResolver;
private static Looper sLooper = null;
//工作线程的handler,即mWorkThreadHandler的handleMessage方法负责操作数据库:
private Handler mWorkerThreadHandler;
//WorkerHandler内部类 :
protected class WorkerHandler extends Handler {
public WorkerHandler(Looper looper) {
super(looper);
}
@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);
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;
}
//向调用者传回信息
//将结果返回给AsyncQueryHandler的handleMessage处理
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);
}
//在startXXX方法中已经制定了target为AsyncQuertHandler本身
reply.sendToTarget();
}
}
//AsyncQueryHandler构造方法:
//AsyncQueryHandler在构造的时候,会创建一个HandlerThread,start之,然后获取其Looper,并利用其Looper构建子线程消息循环,并与mWorkThreadHandler绑定:
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();
}
}
// 与工作者线程绑定的Hanlder
mWorkerThreadHandler = createHandler(sLooper);
}
protected Handler createHandler(Looper looper) {
return new WorkerHandler(looper);
}
//WorkerArgs类:
//这个类提供了WorkerArgs封装数据库操作的请求参数:
protected static final class WorkerArgs {
public Uri uri;
public Handler handler;
public String[] projection;
public String selection;
public String[] selectionArgs;
public String orderBy;
public Object result;
public Object cookie;
public ContentValues values;
}
//startXXX方法:
//当外界调用startXXX方法时,其实会构造WorkerArgs对象,然后将这个对象以消息的形式发送给 mWorkThreadHandler,处理完成后将结果发送给AsyncQueryHandler本身:
//将当前Handler封装进去,发送到HandlerThread中去,HandlerThread绑定的mWorkerHandler,mWorkerHandler负责将打包好的消息发送,并且处理完消息得到结果后,args.handler将结构发送给自己进行处理。(这就是线程间的通信了) 。mWorkerHandler和一个子线程绑定(HanderThread),能够处理比较耗时的操作,AsyncQueryHandler提供异步处理。
public void startQuery(int token, Object cookie, Uri uri,String[] projection, String selection, String[] selectionArgs,String orderBy)
{
// Use the token as what so cancel Operations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_QUERY;
WorkerArgs args = new WorkerArgs();
// 保存调用者Handler对象,以便回调
args.handler = this;//注意这行制定了Handler为本身,即AsyncQueryHandler
args.uri = uri;
args.projection = projection;
args.selection = selection;
args.selectionArgs = selectionArgs;
args.orderBy = orderBy;
args.cookie = cookie;
msg.obj = args;
// 向工作者线程发出处理请求
mWorkerThreadHandler.sendMessage(msg);//交给子线程处理
}
//handleMessage方法
//最终,操作结果交由AsyncQueryHandler类的handleMessage处理:
//AsyncQueryHandler类的handleMessage方法回调了onXXXcomplete系列方法。我们重写onXXXcomplete方法即可拿到数据库操作结果。
@Override
public void handleMessage(Message msg) {
WorkerArgs args = (WorkerArgs) msg.obj;
if (localLOGV) {
Log.d(TAG, "AsyncQueryHandler.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_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封装了调用者线程与工作线程的交互过程。交互的主体是两个Handler,一个运行在调用者线程中,一个运行在工作者线程中。通过提供onXXXComplete的回调接口,实现事件的完成处理。
流程:
startQuery->WorkerHandler::handleMessage->AsyncQueryHandler::handleMessage->onDeleteComplete
AsyncQueryHandler还为我们做了什么:
A,AsyncQueryHandler中使用了一个WeakReference<ContentResolver>对象,即 ContentResolver的弱引用作用:当contentProvied发生变化时候同步更新仍可以通过使用 AsyncQueryHandler类来达到这一要求(暂时还没理解这个作用)
B,同时,在它执行操作数据库时,吃掉了所有的异常。见如下代码:
catch (Exception e) {Log.w(TAG, e.toString());cursor = null;}