AsyncQueryHandler.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content; 18 19import android.database.Cursor; 20import android.net.Uri; 21import android.os.Handler; 22import android.os.HandlerThread; 23import android.os.Looper; 24import android.os.Message; 25import android.util.Log; 26 27/** 28 * A helper class to help make handling asynchronous {@link ContentResolver} 29 * queries easier. 30 */ 31public abstract class AsyncQueryHandler extends Handler { 32 private static final String TAG = "AsyncQuery"; 33 private static final boolean localLOGV = false; 34 35 private static final int EVENT_ARG_QUERY = 1; 36 private static final int EVENT_ARG_INSERT = 2; 37 private static final int EVENT_ARG_UPDATE = 3; 38 private static final int EVENT_ARG_DELETE = 4; 39 40 /* package */ ContentResolver mResolver; 41 42 private static Looper sLooper = null; 43 44 private Handler mWorkerThreadHandler; 45 46 protected static final class WorkerArgs { 47 public Uri uri; 48 public Handler handler; 49 public String[] projection; 50 public String selection; 51 public String[] selectionArgs; 52 public String orderBy; 53 public Object result; 54 public Object cookie; 55 public ContentValues values; 56 } 57 58 protected class WorkerHandler extends Handler { 59 public WorkerHandler(Looper looper) { 60 super(looper); 61 } 62 63 @Override 64 public void handleMessage(Message msg) { 65 WorkerArgs args = (WorkerArgs) msg.obj; 66 67 int token = msg.what; 68 int event = msg.arg1; 69 70 switch (event) { 71 case EVENT_ARG_QUERY: 72 Cursor cursor; 73 try { 74 cursor = mResolver.query(args.uri, args.projection, 75 args.selection, args.selectionArgs, 76 args.orderBy); 77 } catch (Exception e) { 78 cursor = null; 79 } 80 81 args.result = cursor; 82 break; 83 84 case EVENT_ARG_INSERT: 85 args.result = mResolver.insert(args.uri, args.values); 86 break; 87 88 case EVENT_ARG_UPDATE: 89 int r = mResolver.update(args.uri, args.values, args.selection, 90 args.selectionArgs); 91 args.result = new Integer(r); 92 break; 93 94 case EVENT_ARG_DELETE: 95 int r2 = mResolver.delete(args.uri, args.selection, args.selectionArgs); 96 args.result = new Integer(r2); 97 break; 98 99 } 100 101 // passing the original token value back to the caller 102 // on top of the event values in arg1. 103 Message reply = args.handler.obtainMessage(token); 104 reply.obj = args; 105 reply.arg1 = msg.arg1; 106 107 if (localLOGV) { 108 Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1 109 + ", reply.what=" + reply.what); 110 } 111 112 reply.sendToTarget(); 113 } 114 } 115 116 public AsyncQueryHandler(ContentResolver cr) { 117 super(); 118 mResolver = cr; 119 synchronized (AsyncQueryHandler.class) { 120 if (sLooper == null) { 121 HandlerThread thread = new HandlerThread("AsyncQueryWorker"); 122 thread.start(); 123 124 sLooper = thread.getLooper(); 125 } 126 } 127 mWorkerThreadHandler = createHandler(sLooper); 128 } 129 130 protected Handler createHandler(Looper looper) { 131 return new WorkerHandler(looper); 132 } 133 134 /** 135 * This method begins an asynchronous query. When the query is done 136 * {@link #onQueryComplete} is called. 137 * 138 * @param token A token passed into {@link #onQueryComplete} to identify 139 * the query. 140 * @param cookie An object that gets passed into {@link #onQueryComplete} 141 */ 142 public void startQuery(int token, Object cookie, Uri uri, 143 String[] projection, String selection, String[] selectionArgs, 144 String orderBy) { 145 // Use the token as what so cancelOperations works properly 146 Message msg = mWorkerThreadHandler.obtainMessage(token); 147 msg.arg1 = EVENT_ARG_QUERY; 148 149 WorkerArgs args = new WorkerArgs(); 150 args.handler = this; 151 args.uri = uri; 152 args.projection = projection; 153 args.selection = selection; 154 args.selectionArgs = selectionArgs; 155 args.orderBy = orderBy; 156 args.cookie = cookie; 157 msg.obj = args; 158 159 mWorkerThreadHandler.sendMessage(msg); 160 } 161 162 /** 163 * Attempts to cancel operation that has not already started. Note that 164 * there is no guarantee that the operation will be canceled. They still may 165 * result in a call to on[Query/Insert/Update/Delete]Complete after this 166 * call has completed. 167 * 168 * @param token The token representing the operation to be canceled. 169 * If multiple operations have the same token they will all be canceled. 170 */ 171 public final void cancelOperation(int token) { 172 mWorkerThreadHandler.removeMessages(token); 173 } 174 175 /** 176 * This method begins an asynchronous insert. When the insert operation is 177 * done {@link #onInsertComplete} is called. 178 * 179 * @param token A token passed into {@link #onInsertComplete} to identify 180 * the insert operation. 181 * @param cookie An object that gets passed into {@link #onInsertComplete} 182 * @param uri the Uri passed to the insert operation. 183 * @param initialValues the ContentValues parameter passed to the insert operation. 184 */ 185 public final void startInsert(int token, Object cookie, Uri uri, 186 ContentValues initialValues) { 187 // Use the token as what so cancelOperations works properly 188 Message msg = mWorkerThreadHandler.obtainMessage(token); 189 msg.arg1 = EVENT_ARG_INSERT; 190 191 WorkerArgs args = new WorkerArgs(); 192 args.handler = this; 193 args.uri = uri; 194 args.cookie = cookie; 195 args.values = initialValues; 196 msg.obj = args; 197 198 mWorkerThreadHandler.sendMessage(msg); 199 } 200 201 /** 202 * This method begins an asynchronous update. When the update operation is 203 * done {@link #onUpdateComplete} is called. 204 * 205 * @param token A token passed into {@link #onUpdateComplete} to identify 206 * the update operation. 207 * @param cookie An object that gets passed into {@link #onUpdateComplete} 208 * @param uri the Uri passed to the update operation. 209 * @param values the ContentValues parameter passed to the update operation. 210 */ 211 public final void startUpdate(int token, Object cookie, Uri uri, 212 ContentValues values, String selection, String[] selectionArgs) { 213 // Use the token as what so cancelOperations works properly 214 Message msg = mWorkerThreadHandler.obtainMessage(token); 215 msg.arg1 = EVENT_ARG_UPDATE; 216 217 WorkerArgs args = new WorkerArgs(); 218 args.handler = this; 219 args.uri = uri; 220 args.cookie = cookie; 221 args.values = values; 222 args.selection = selection; 223 args.selectionArgs = selectionArgs; 224 msg.obj = args; 225 226 mWorkerThreadHandler.sendMessage(msg); 227 } 228 229 /** 230 * This method begins an asynchronous delete. When the delete operation is 231 * done {@link #onDeleteComplete} is called. 232 * 233 * @param token A token passed into {@link #onDeleteComplete} to identify 234 * the delete operation. 235 * @param cookie An object that gets passed into {@link #onDeleteComplete} 236 * @param uri the Uri passed to the delete operation. 237 * @param selection the where clause. 238 */ 239 public final void startDelete(int token, Object cookie, Uri uri, 240 String selection, String[] selectionArgs) { 241 // Use the token as what so cancelOperations works properly 242 Message msg = mWorkerThreadHandler.obtainMessage(token); 243 msg.arg1 = EVENT_ARG_DELETE; 244 245 WorkerArgs args = new WorkerArgs(); 246 args.handler = this; 247 args.uri = uri; 248 args.cookie = cookie; 249 args.selection = selection; 250 args.selectionArgs = selectionArgs; 251 msg.obj = args; 252 253 mWorkerThreadHandler.sendMessage(msg); 254 } 255 256 /** 257 * Called when an asynchronous query is completed. 258 * 259 * @param token the token to identify the query, passed in from 260 * {@link #startQuery}. 261 * @param cookie the cookie object that's passed in from {@link #startQuery}. 262 * @param cursor The cursor holding the results from the query. 263 */ 264 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 265 // Empty 266 } 267 268 /** 269 * Called when an asynchronous insert is completed. 270 * 271 * @param token the token to identify the query, passed in from 272 * {@link #startInsert}. 273 * @param cookie the cookie object that's passed in from 274 * {@link #startInsert}. 275 * @param uri the uri returned from the insert operation. 276 */ 277 protected void onInsertComplete(int token, Object cookie, Uri uri) { 278 // Empty 279 } 280 281 /** 282 * Called when an asynchronous update is completed. 283 * 284 * @param token the token to identify the query, passed in from 285 * {@link #startUpdate}. 286 * @param cookie the cookie object that's passed in from 287 * {@link #startUpdate}. 288 * @param result the result returned from the update operation 289 */ 290 protected void onUpdateComplete(int token, Object cookie, int result) { 291 // Empty 292 } 293 294 /** 295 * Called when an asynchronous delete is completed. 296 * 297 * @param token the token to identify the query, passed in from 298 * {@link #startDelete}. 299 * @param cookie the cookie object that's passed in from 300 * {@link #startDelete}. 301 * @param result the result returned from the delete operation 302 */ 303 protected void onDeleteComplete(int token, Object cookie, int result) { 304 // Empty 305 } 306 307 @Override 308 public void handleMessage(Message msg) { 309 WorkerArgs args = (WorkerArgs) msg.obj; 310 311 if (localLOGV) { 312 Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what 313 + ", msg.arg1=" + msg.arg1); 314 } 315 316 int token = msg.what; 317 int event = msg.arg1; 318 319 // pass token back to caller on each callback. 320 switch (event) { 321 case EVENT_ARG_QUERY: 322 onQueryComplete(token, args.cookie, (Cursor) args.result); 323 break; 324 325 case EVENT_ARG_INSERT: 326 onInsertComplete(token, args.cookie, (Uri) args.result); 327 break; 328 329 case EVENT_ARG_UPDATE: 330 onUpdateComplete(token, args.cookie, (Integer) args.result); 331 break; 332 333 case EVENT_ARG_DELETE: 334 onDeleteComplete(token, args.cookie, (Integer) args.result); 335 break; 336 } 337 } 338} 339