Handler官方范例AsyncQueryHandler源码解析
发布日期:2021-11-09 22:50:58 浏览次数:29 分类:技术文章

本文共 19000 字,大约阅读时间需要 63 分钟。

在阅读本文之前,你需要了解Handler作为Android中的线程间通信机制究竟是如何运作的,可以参考

源码

/** * A helper class to help make handling asynchronous {@link ContentResolver} * queries easier. */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
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
(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; } }}

背景

/** * A helper class to help make handling asynchronous {
@link ContentResolver} * queries easier. */

这是07年的代码,从最开头的类注释来看,写这个helper类的目的是用于简化ContentResolver异步查询。出于历史因素,你可以在某些Android应用——比如Dialer——源码中看到它的身影。不过就实际而言,后来出现的Loader在可代替的情况下比它更好用。

虽然如此,作为官方范例,在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); }

它做了这样几件事:

1. super()调用父亲构造方法,令此AsyncQueryHandler工作在主线程中(参见Handler源码,其中有mLooper = Looper.myLooper();)。
2. 将ContentResolver作为弱引用持有
3. 在类同步语句块中,如果由static修饰的sLooper为null,创建一个工作线程thread,然后得到sLooper的值。
参考资料:
4. 通过sLooper,创建在工作线程中工作的mWorkerThreadHandler 。因为是同一个sLooper,所以不同AsyncQueryHandler实例的mWorkerThreadHandler 工作在同一个工作线程中

HandlerThread专门设计用于与Handler协同工作,从设计角度而言,这样关系紧密的两个类,要么用A持有B的方式扩展使用,要么就反过来。这里是由Handler持有HandlerThread,与HandlerThread持有Handler的方式相比各有优缺点。

这里封装的具体操作无非是増删改查,我们看一种就好:

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

注释已经十分详细,做的事情也十分简单,用obtainMessage()从循环池中拿出一个Message,放上表示动作的标志位msg.arg1 = EVENT_ARG_QUERY;,然后构造一个专门用于放数据的静态内部类WorkerArgs ,传入的参数全部放入其中,再作为Object传给Messagemsg.obj = args;,最后是发出MessagemWorkerThreadHandler.sendMessage(msg);

再看下WorkerThreadHandler是如何工作的:

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

根据传来的msg.arg1;判定是什么事件,处理后通过args.handler即AsyncQueryHandler本身构造回复用的Message,传入数据的同时reply.obj = args;继续传入标志位reply.arg1 = msg.arg1;,然后回复即可。

注意这里告诉了我们一个使用Cursor时提速的技巧:

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

最后就是主线程中的处理了:

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

通过标志位判定,来进行増删改查后的回调处理。

本篇至此结束,下次见。

转载地址:https://blog.csdn.net/dbnight/article/details/50688336 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:「深入Java」Annotation注解
下一篇:「打造自己的Library」SharedPreferences篇

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月14日 18时56分13秒