美文网首页成长●随笔Android开发手机移动程序开发
Android 异步查询框架AsyncQueryHandler分

Android 异步查询框架AsyncQueryHandler分

作者: 大荣言午 | 来源:发表于2017-07-10 16:03 被阅读0次

    Android 异步查询框架AsyncQueryHandler分析

    在一般的应用中可以使用ContentProvider去操作数据库。
    这在数据量很小的时候是没有问题的,但是如果数据量大了,可能导致UI线程发生ANR异常(超过5秒)。
    当然你也可以写个Handler去做这些操作,只是你每次使用ContentProvider时都要再写个Handler,必然降低了效率。
    因此当数据量较大时,最好还是使用android已经封装好的异步查询框架AsyncQueryHandler,优化我们的代码.AsyncQueryHandler类封装了两个线程的交互过程。它们是调用者线程与工作线程。这两个线程里都维护着一个handler,通过提供onXXXComplete的回调接口,实现事件的完成处理。在处理读取联系人、通话记录相关,也解决的部分山寨手机兼容问题。

    基本用法

    自定义MyQueryHandler继承AsyncQueryHandler
    实现里面的两个方法,构造方法和onQueryComplete方法

    private class MyAsyncQueryHandler extends AsyncQueryHandler {
    
        public MyAsyncQueryHandler(ContentResolver cr) {
            super(cr);
        }
    
        @Override
        public void startQuery(int tag, Object cookie, Uri uri, String[] projection, String selection,
                String[] selectionArgs, String orderBy) {
            super.startQuery(tag, cookie, uri, projection, selection, selectionArgs, orderBy);
            Log.e("TAG", "startQuery");
        }
    
        protected void onQueryComplete(final int tag, final Object cookie, final Cursor cursor) {
            Log.e("TAG", "onQueryComplete");
            super.onQueryComplete(tag, cookie, cursor);
        }
    
    }
    }
    

    一般开始调用的方法: myAsyncQueryHandler.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);

    以上方法参数:

    • token
      令牌,主要用来标识查询,保证唯一

    • cookie
      你想传给onXXXComplete方法使用的一个对象。(没有的话传递null即可)

    • Uri uri(进行查询的通用资源标志符):

    • projection 查询的列

    • selection 限制条件

    • selectionArgs 查询参数

    • orderBy 排序条件

    参考代码:

    Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; // 联系人Uri;
        // 查询的字段
        String[] projection = { ContactsContract.CommonDataKinds.Phone._ID,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.DATA1,
                "sort_key", ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                ContactsContract.CommonDataKinds.Phone.PHOTO_ID, ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY };
        // 按照sort_key升序查詢
        asyncQueryHandler.startQuery(tag, cookie, uri, projection, null, null, "sort_key COLLATE LOCALIZED asc");
    

    AsyncQueryHandler类的源码

    从整体上把握这个类:每个AsyncQueryHandler对象都会开启一个后台线程,在线程中执行与ContentProvider组件的数据交互,进行增删改查。调用时,可以通过AsyncQueryHandler.startXXX系列方法将请求打包发送到后台线程,当相关处理完成后,会将结果异步回传给主线程并调用AsyncQueryHandler.onXXXComplete方法通知调用者。调用者每次请求时,需要传入一个整型值token作为这次请求的标识,当该请求完成后进行回调时,会将token传回,帮助调用者确定这是哪一次请求。参考博文图

    源码如下:

    public abstract class AsyncQueryHandler extends Handler {
    private static final String TAG = "AsyncQuery";
    private static final boolean localLOGV = false;
    
    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;
    
    /* package */ final WeakReference<ContentResolver> mResolver;
    
    private static Looper sLooper = null;
    
    private Handler mWorkerThreadHandler;
    
    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;
    }
    
    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();
        }
    }
    
    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);
    }
    
    /**
     * 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);
    }
    
    /**
     * Attempts to cancel operation that has not already started. Note that
     * there is no guarantee that the operation will be canceled. They still may
     * result in a call to on[Query/Insert/Update/Delete]Complete after this
     * call has completed.
     *
     * @param token The token representing the operation to be canceled.
     *  If multiple operations have the same token they will all be canceled.
     */
    public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
    }
    
    /**
     * This method begins an asynchronous insert. When the insert operation is
     * done {@link #onInsertComplete} is called.
     *
     * @param token A token passed into {@link #onInsertComplete} to identify
     *  the insert operation.
     * @param cookie An object that gets passed into {@link #onInsertComplete}
     * @param uri the Uri passed to the insert operation.
     * @param initialValues the ContentValues parameter passed to the insert operation.
     */
    public final void startInsert(int token, Object cookie, Uri uri,
            ContentValues initialValues) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_INSERT;
    
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = initialValues;
        msg.obj = args;
    
        mWorkerThreadHandler.sendMessage(msg);
    }
    
    /**
     * This method begins an asynchronous update. When the update operation is
     * done {@link #onUpdateComplete} is called.
     *
     * @param token A token passed into {@link #onUpdateComplete} to identify
     *  the update operation.
     * @param cookie An object that gets passed into {@link #onUpdateComplete}
     * @param uri the Uri passed to the update operation.
     * @param values the ContentValues parameter passed to the update operation.
     */
    public final void startUpdate(int token, Object cookie, Uri uri,
            ContentValues values, String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_UPDATE;
    
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = values;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
    
        mWorkerThreadHandler.sendMessage(msg);
    }
    
    /**
     * This method begins an asynchronous delete. When the delete operation is
     * done {@link #onDeleteComplete} is called.
     *
     * @param token A token passed into {@link #onDeleteComplete} to identify
     *  the delete operation.
     * @param cookie An object that gets passed into {@link #onDeleteComplete}
     * @param uri the Uri passed to the delete operation.
     * @param selection the where clause.
     */
    public final void startDelete(int token, Object cookie, Uri uri,
            String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_DELETE;
    
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
    
        mWorkerThreadHandler.sendMessage(msg);
    }
    
    /**
     * Called when an asynchronous query is completed.
     *
     * @param token the token to identify the query, passed in from
     *            {@link #startQuery}.
     * @param cookie the cookie object passed in from {@link #startQuery}.
     * @param cursor The cursor holding the results from the query.
     */
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        // Empty
    }
    
    /**
     * Called when an asynchronous insert is completed.
     *
     * @param token the token to identify the query, passed in from
     *        {@link #startInsert}.
     * @param cookie the cookie object that's passed in from
     *        {@link #startInsert}.
     * @param uri the uri returned from the insert operation.
     */
    protected void onInsertComplete(int token, Object cookie, Uri uri) {
        // Empty
    }
    
    /**
     * Called when an asynchronous update is completed.
     *
     * @param token the token to identify the query, passed in from
     *        {@link #startUpdate}.
     * @param cookie the cookie object that's passed in from
     *        {@link #startUpdate}.
     * @param result the result returned from the update operation
     */
    protected void onUpdateComplete(int token, Object cookie, int result) {
        // Empty
    }
    
    /**
     * Called when an asynchronous delete is completed.
     *
     * @param token the token to identify the query, passed in from
     *        {@link #startDelete}.
     * @param cookie the cookie object that's passed in from
     *        {@link #startDelete}.
     * @param result the result returned from the delete operation
     */
    protected void onDeleteComplete(int token, Object cookie, int result) {
        // Empty
    }
    
    @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是个抽象类,不能直接被实例化;


    争取每日把之前印象笔记中的碎片点,总结出来分享给各位大佬,相互督促相互学习进步,欢迎各位老铁留言……

    相关文章

      网友评论

        本文标题:Android 异步查询框架AsyncQueryHandler分

        本文链接:https://www.haomeiwen.com/subject/khfuhxtx.html