Android AsyncQueryHandler

648 阅读5分钟

​ 最近在做项目的时候,查看前辈写的代码,发现了AsyncQueryHandler.startQuery()这种写法。

由于以前一直没有使用过AsyncQueryHandler这个类,所以就根据网上查找到的信息和自己查看源码的的理解写下这篇文章。

本文的理解比较浅显,如果有理解错误,欢迎指正。

AsyncQueryHandler的简介

根据源码中的说明,可以很明显的发现AsyncQueryHandler是一个异步处理ContentProvider的抽象帮助类,该类可以实现ContentProvider的增删改查,虽然根据类名和说明的话只能体现查这个功能,但是我为什么这样说呢,可以看下面的WorkerHandler这个章节。

 * A helper class to help make handling asynchronous {@link ContentResolver}
 * queries easier.
 */
 public abstract class AsyncQueryHandler extends Handler {

我们一般在处理ContentProvider是直接通过ContentResolver直接处理的。在小数据的时候,这种方式是没有什么问题的。

但是当我们需要处理的数据是很大的时候,如果直接通过ContentResolver在UI线程中直接处理的话,很有可能会发生ANR异常(超过5秒)。

当然你也可以写个Handler去做这些操作,但是Android已经封装好异步查询框架AsyncQueryHandler,所以为什么不使用AsyncQueryHandler来让我们事半功倍呢。

AsyncQueryHandler实现原理

HandlerThread 查看AsyncQueryHandler的构造函数,发现首先构建一个HandlerThread,并且获取这个HandlerThread的Looper,然后通过Looper创建Handler。

所以AsyncQueryHandler构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了,这样我们的Looper+Handler+MessageQueue+Thread异步循环机制构建完成。如果对于HandlerThread有什么不懂的,可以看看blog.csdn.net/javazejian/…

public AsyncQueryHandler(ContentResolver cr) {
    super();
    mResolver = new WeakReference<ContentResolver>(cr);
    synchronized (AsyncQueryHandler.class) {
        if (sLooper == null) {
            HandlerThread thread = new HandlerThread("AsyncQueryWorker");
            thread.start();

            sLooper = thread.getLooper();
        }
    }
    mWorkerThreadHandler = createHandler(sLooper);
}

protected Handler createHandler(Looper looper) {
    return new WorkerHandler(looper);
}

WorkerHandler

通过上面的代码,可以知道AsyncQueryHandler操作ContentProvider的Hanlder是WorkerHandler,那么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);
                    // 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();
    }
}

可以明显的看到WorkerHandler就是一个简单的继承自Handler的内部类。在这个类中根据传递给Handler的Message的event进行区分,实现ContentProvider的增删改查。

实现

这边以查这个动作简单讲解下AsyncQueryHandler是怎么实现的。

根据上面的代码,查的Message event是EVENT_ARG_QUERY,所以我们继续在AsyncQueryHandler中查看代码,发现如下两块代码:

/**
 * This method begins an asynchronous query. When the query is done
 * {@link #onQueryComplete} is called.
 *
 * @param token A token passed into {@link #onQueryComplete} to identify
 *  the query.
 * @param cookie An object that gets passed into {@link #onQueryComplete}
 * @param uri The URI, using the content:// scheme, for the content to
 *         retrieve.
 * @param projection A list of which columns to return. Passing null will
 *         return all columns, which is discouraged to prevent reading data
 *         from storage that isn't going to be used.
 * @param selection A filter declaring which rows to return, formatted as an
 *         SQL WHERE clause (excluding the WHERE itself). Passing null will
 *         return all rows for the given URI.
 * @param selectionArgs You may include ?s in selection, which will be
 *         replaced by the values from selectionArgs, in the order that they
 *         appear in the selection. The values will be bound as Strings.
 * @param orderBy How to order the rows, formatted as an SQL ORDER BY
 *         clause (excluding the ORDER BY itself). Passing null will use the
 *         default sort order, which may be unordered.
 */
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
    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);
}
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
@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;
    }
}

通过上面的两块代码和WorkerHandler中的代码,我们可以很清晰的知道AsyncQueryHandler对于查这个操作的流程。

在startQuery方法中,将该方法的参数都放进了args对象中,同时将AsyncQueryHandler这个类对象本身也放进了args对象中(arg.handler=this)。将args对象放进msg对象中,再通过WorkerHandler的sendMessage()方法把Message传递给WorkerHandler进行处理。在WorkerHandler中,通过ContentResolver查询ContentProvider,并把查询到的Cursor作为结果传递给Message,再通过Message的sendToTarget()方法把Message传递给AsyncQueryHandler,AsyncQueryHandler中复写的handleMessage()方法会处理刚才传递过来的Message。

这边借用下woaishitu文章中图,可以清晰看出调用流程。感谢

img-blog.csdn.net/20180906224…

使用

下面使用一些简单的代码,进行下AsyncQueryHandler的使用。

public class TestClass { private AsyncQueryHandler mHandler;

private TestClass(Context context) {
    mHandler = new AsyncQueryHandler(mContext.getContentResolver()) {
        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            super.onQueryComplete(token, cookie, cursor);
            // 这边进行自己想要的操作,比如更新界面
            XXXXXXXXXXXXXXXX
        }
    }
}

public void testStartQuery1(Context comHandler.startQuery(
    mHandler.startQuery(
            token1,   // 在onQueryComplete中区分哪个startQuery调用的回调,即onQueryComplete的第一个参数
            cookie1,  // 从startQuery传递给onQueryComplete的一个Object对象,即onQueryComplete的第二个参数
            Uri1,     // ContentResolver.query的第一个参数:Uri
            new String1[] {
                XXXXXX
            },       // ContentResolver.query的第二个参数:projection
            null,    // ContentResolver.query的第三个参数:selection
            null,    // ContentResolver.query的第四个参数:selectionArgs
            null     // ContentResolver.query的第五个参数:sortOrder
    );
}

public void testStartQuery2(Context comHandler.startQuery(
    mHandler.startQuery(
            token2,   // 在onQueryComplete中区分哪个startQuery调用的回调,即onQueryComplete的第一个参数
            cookie2,  // 从startQuery传递给onQueryComplete的一个Object对象,即onQueryComplete的第二个参数
            Uri2,     // ContentResolver.query的第一个参数:Uri
            new String2[] {
                XXXXXX
            },       // ContentResolver.query的第二个参数:projection
            null,    // ContentResolver.query的第三个参数:selection
            null,    // ContentResolver.query的第四个参数:selectionArgs
            null     // ContentResolver.query的第五个参数:sortOrder
    );
}

感谢参照文献的三位作者。分别是:

woaishitu:blog.csdn.net/weixin_4219…

时之沙:blog.csdn.net/t12x3456/ar…

zejian_:blog.csdn.net/javazejian/…