AsyncQueryHandler代码分析

AsyncQueryHandler代码分析

一、概述

异步的查询操作帮助类,可以处理增删改查(ContentProvider提供的数据)。查询数据库,如果数据太多会造成主线程堵塞导致ANR,所以需要开启子线程,然后 用Handler将结果回传。而使用android系统提供的AsyncQueryHandler方便的实现这个过程。

二、AsyncQueryHandler

#2-1 源代码

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

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

    public void startQuery(int token, Object cookie, Uri uri,
            String[] projection, String selection, String[] selectionArgs,
            String orderBy) {
        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);
    }

    public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
    }

    public final void startInsert(int token, Object cookie, Uri uri,
            ContentValues initialValues) {
        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);
    }

    public final void startUpdate(int token, Object cookie, Uri uri,
            ContentValues values, String selection, String[] selectionArgs) {
        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);
    }

    public final void startDelete(int token, Object cookie, Uri uri,
            String selection, String[] selectionArgs) {
        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);
    }

    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        // Empty
    }

    protected void onInsertComplete(int token, Object cookie, Uri uri) {
        // Empty
    }

    protected void onUpdateComplete(int token, Object cookie, int result) {
        // Empty
    }

    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;

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

#2-2 构造方法

构造方法里初始化子线程Looper,使用AsyncQueryHandler处理的任务都运行在该子线程中。AsyncQueryHandler本身也可以在多线程里使用,所以构造方法里使用了synchronized 避免创建时出现多线程问题。值得注意的是多线程编程时,一定要多考虑静态成员变量在多线程下的不同步问题。

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

#2-3 增、删、改、查

增、删、改、查操作通过WorkerArgs和WorkerThreadHandler运行在子线程里,并且在WorkerArgs里传入当前AsyncQueryHandler 引用用于子线程操作完成后回调到传入的AsyncQueryHandler 里。

    #AsyncQueryHandler

    public void startQuery(int token, Object cookie, Uri uri,
            String[] projection, String selection, String[] selectionArgs,
            String orderBy) {
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_QUERY;
        ......
        mWorkerThreadHandler.sendMessage(msg);
    }

-----------------------------------------------
        #WorkerThreadHandler

        @Override
        public void handleMessage(Message msg) {
            ......
            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;
                    }
           ......


        }
     }

#2-4 操作流程

查询时把token和EVENT_ARG_QUERY重要参数传入到子线程handleMessage里,子线程handleMessage处理完实际的查询操作再把token和EVENT_ARG_QUERY回传到AsyncQueryHandler
里,并且通过token和EVENT_ARG_QUERY来识别是哪一次查询。

    #AsyncQueryHandler

    public void startQuery(int token, Object cookie, Uri uri,
            String[] projection, String selection, String[] selectionArgs,
            String orderBy) {
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_QUERY;
        ......
        mWorkerThreadHandler.sendMessage(msg);
    }
--------------------------------------------
            #WorkerThreadHandler

            ......
            WorkerArgs args = (WorkerArgs) msg.obj;

            int token = msg.what;
            int event = msg.arg1;
            //实际查询操作
            ......
            //查询完后将token和event再传回到AsyncQueryHandler 
            Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;
            ......
            reply.sendToTarget();
        }

------------------------------------------
        #AsyncQueryHandler

        ......
        int token = msg.what;
        int event = msg.arg1;

        switch (event) {
            case EVENT_ARG_QUERY:
                onQueryComplete(token, args.cookie, (Cursor) args.result);
                break;
        ......

        }

三、知识点总结

#3-1 多线程同步

使用一个Looper是保证通过AsyncQueryHandler处理的任务都运行在同一个子线程里;因为AsyncQueryHandler本身可以在多线程里执行,所以sLooper 需要做了一个同步的处理,避免多线程引起判断不正确的问题。

       synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();

                sLooper = thread.getLooper();
            }
        }

#3-2 异步回调机制

AsyncQueryHandler整个代码结构非常清晰和简洁,整个操作流程采用了两个Handler相互通信实现。AsyncQueryHandler发送消息到WorkerThreadHandler,然后在WorkerThreadHandler处理完实际操作后再回发消息到AsyncQueryHandler完成一次通信流程。

#3-3 思考

顾名思义AsyncQueryHandler用于异步查询操作,且是单线程查询操作,不适合用于查询大量网络信息的APP;考虑到实际使用场景AsyncQueryHandler的使用范围也有一定的限制,但是我们可以参照AsyncQueryHandler写一个比如说AsyncHandler比较通用的异步处理帮助类。

猜你喜欢

转载自blog.csdn.net/wcs542882916/article/details/78447447