AsyncQueryHandler详解及使用

AsyncQueryHandler详解及使用

  • AsyncQueryHandler详解及使用
    • AsyncQueryHandler概述
    • AsyncQueryHandler实现原理
    • AsyncQueryHandler的使用


AsyncQueryHandler概述

项目中,用到了AsyncQueryHandler,所以在这里将自己对它的理解写出来,做一个总结。如果有错误的地方,或者理解不到位的地方,请各位高手批评指正~

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详解及使用_第1张图片

AsyncQueryHandler的使用

很简单,就两步:
- 继承AsyncQueryHandler类,并实现onXXXComplete方法。
onXXXComplete方法中主要是处理数据库操作结果,用以完成界面更新。如果不处理结果的话,也可以不实现onXXXComplete方法。
- 利用AsyncQueryHandler实例直接调用startxxx()方法。

在使用startQuery()方法时,各个参数的含义需要说明一下(因为我第一次用的时候就很迷惑。。。):
token:一个令牌,像是”虎符”的作用吧,同一个AsyncQueryHandler类对象中,startQuery()和onQueryComplete()方法的token应该是一致的。
cookie: 这个是在startQuery()中传入,想在onQueryComplete()中使用的对象。没有的话传递null即可。
uri: 这个是想要操作的数据库对应的uri
projection: 想要查询的列
selection: 限制条件
selectionArgs: 限制条件的具体值
orderBy: 排序条件

另外,在实际开发过程中,我们不仅可以将AsyncQueryHandler拿来直接用,还可以根据自己的需要,写一个类似的抽象类,从而让传递的参数更灵活,实现更多的代码需求。

你可能感兴趣的:(Android)