Android-AsyncQueryHandler 详解【整理】

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;}
 

猜你喜欢

转载自blog.csdn.net/fanleiym/article/details/83783949