blob: 07da99d9d08bd4bc206d6c0763e51932aca43205 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
27import java.lang.ref.WeakReference;
28
29/**
30 * A helper class to help make handling asynchronous {@link ContentResolver}
31 * queries easier.
32 */
33public abstract class AsyncQueryHandler extends Handler {
34 private static final String TAG = "AsyncQuery";
35 private static final boolean localLOGV = false;
36
37 private static final int EVENT_ARG_QUERY = 1;
38 private static final int EVENT_ARG_INSERT = 2;
39 private static final int EVENT_ARG_UPDATE = 3;
40 private static final int EVENT_ARG_DELETE = 4;
Jeff Sharkeya5f743f2009-08-12 09:53:48 -070041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 /* package */ final WeakReference<ContentResolver> mResolver;
43
44 private static Looper sLooper = null;
45
46 private Handler mWorkerThreadHandler;
47
48 protected static final class WorkerArgs {
49 public Uri uri;
50 public Handler handler;
51 public String[] projection;
52 public String selection;
53 public String[] selectionArgs;
54 public String orderBy;
55 public Object result;
56 public Object cookie;
57 public ContentValues values;
58 }
59
60 protected class WorkerHandler extends Handler {
61 public WorkerHandler(Looper looper) {
62 super(looper);
63 }
64
65 @Override
66 public void handleMessage(Message msg) {
67 final ContentResolver resolver = mResolver.get();
68 if (resolver == null) return;
69
70 WorkerArgs args = (WorkerArgs) msg.obj;
71
72 int token = msg.what;
73 int event = msg.arg1;
74
75 switch (event) {
76 case EVENT_ARG_QUERY:
77 Cursor cursor;
78 try {
79 cursor = resolver.query(args.uri, args.projection,
80 args.selection, args.selectionArgs,
81 args.orderBy);
82 // Calling getCount() causes the cursor window to be filled,
83 // which will make the first access on the main thread a lot faster.
84 if (cursor != null) {
85 cursor.getCount();
86 }
87 } catch (Exception e) {
Daisuke Miyakawa62fd4e32010-11-18 10:54:00 -080088 Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 cursor = null;
90 }
91
92 args.result = cursor;
93 break;
94
95 case EVENT_ARG_INSERT:
96 args.result = resolver.insert(args.uri, args.values);
97 break;
98
99 case EVENT_ARG_UPDATE:
100 args.result = resolver.update(args.uri, args.values, args.selection,
101 args.selectionArgs);
102 break;
103
104 case EVENT_ARG_DELETE:
105 args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
106 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 }
108
109 // passing the original token value back to the caller
110 // on top of the event values in arg1.
111 Message reply = args.handler.obtainMessage(token);
112 reply.obj = args;
113 reply.arg1 = msg.arg1;
114
115 if (localLOGV) {
116 Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
117 + ", reply.what=" + reply.what);
118 }
119
120 reply.sendToTarget();
121 }
122 }
123
124 public AsyncQueryHandler(ContentResolver cr) {
125 super();
126 mResolver = new WeakReference<ContentResolver>(cr);
127 synchronized (AsyncQueryHandler.class) {
128 if (sLooper == null) {
129 HandlerThread thread = new HandlerThread("AsyncQueryWorker");
130 thread.start();
Jeff Sharkeya5f743f2009-08-12 09:53:48 -0700131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 sLooper = thread.getLooper();
133 }
134 }
135 mWorkerThreadHandler = createHandler(sLooper);
136 }
137
138 protected Handler createHandler(Looper looper) {
139 return new WorkerHandler(looper);
140 }
141
142 /**
143 * This method begins an asynchronous query. When the query is done
144 * {@link #onQueryComplete} is called.
145 *
146 * @param token A token passed into {@link #onQueryComplete} to identify
147 * the query.
148 * @param cookie An object that gets passed into {@link #onQueryComplete}
149 * @param uri The URI, using the content:// scheme, for the content to
150 * retrieve.
151 * @param projection A list of which columns to return. Passing null will
152 * return all columns, which is discouraged to prevent reading data
153 * from storage that isn't going to be used.
154 * @param selection A filter declaring which rows to return, formatted as an
155 * SQL WHERE clause (excluding the WHERE itself). Passing null will
156 * return all rows for the given URI.
157 * @param selectionArgs You may include ?s in selection, which will be
158 * replaced by the values from selectionArgs, in the order that they
159 * appear in the selection. The values will be bound as Strings.
160 * @param orderBy How to order the rows, formatted as an SQL ORDER BY
161 * clause (excluding the ORDER BY itself). Passing null will use the
162 * default sort order, which may be unordered.
163 */
164 public void startQuery(int token, Object cookie, Uri uri,
165 String[] projection, String selection, String[] selectionArgs,
166 String orderBy) {
167 // Use the token as what so cancelOperations works properly
168 Message msg = mWorkerThreadHandler.obtainMessage(token);
169 msg.arg1 = EVENT_ARG_QUERY;
170
171 WorkerArgs args = new WorkerArgs();
172 args.handler = this;
173 args.uri = uri;
174 args.projection = projection;
175 args.selection = selection;
176 args.selectionArgs = selectionArgs;
177 args.orderBy = orderBy;
178 args.cookie = cookie;
179 msg.obj = args;
180
181 mWorkerThreadHandler.sendMessage(msg);
182 }
183
184 /**
185 * Attempts to cancel operation that has not already started. Note that
186 * there is no guarantee that the operation will be canceled. They still may
187 * result in a call to on[Query/Insert/Update/Delete]Complete after this
188 * call has completed.
189 *
190 * @param token The token representing the operation to be canceled.
191 * If multiple operations have the same token they will all be canceled.
192 */
193 public final void cancelOperation(int token) {
194 mWorkerThreadHandler.removeMessages(token);
195 }
196
197 /**
198 * This method begins an asynchronous insert. When the insert operation is
199 * done {@link #onInsertComplete} is called.
200 *
201 * @param token A token passed into {@link #onInsertComplete} to identify
202 * the insert operation.
203 * @param cookie An object that gets passed into {@link #onInsertComplete}
204 * @param uri the Uri passed to the insert operation.
205 * @param initialValues the ContentValues parameter passed to the insert operation.
206 */
207 public final void startInsert(int token, Object cookie, Uri uri,
208 ContentValues initialValues) {
209 // Use the token as what so cancelOperations works properly
210 Message msg = mWorkerThreadHandler.obtainMessage(token);
211 msg.arg1 = EVENT_ARG_INSERT;
212
213 WorkerArgs args = new WorkerArgs();
214 args.handler = this;
215 args.uri = uri;
216 args.cookie = cookie;
217 args.values = initialValues;
218 msg.obj = args;
219
220 mWorkerThreadHandler.sendMessage(msg);
221 }
222
223 /**
224 * This method begins an asynchronous update. When the update operation is
225 * done {@link #onUpdateComplete} is called.
226 *
227 * @param token A token passed into {@link #onUpdateComplete} to identify
228 * the update operation.
229 * @param cookie An object that gets passed into {@link #onUpdateComplete}
230 * @param uri the Uri passed to the update operation.
231 * @param values the ContentValues parameter passed to the update operation.
232 */
233 public final void startUpdate(int token, Object cookie, Uri uri,
234 ContentValues values, String selection, String[] selectionArgs) {
235 // Use the token as what so cancelOperations works properly
236 Message msg = mWorkerThreadHandler.obtainMessage(token);
237 msg.arg1 = EVENT_ARG_UPDATE;
238
239 WorkerArgs args = new WorkerArgs();
240 args.handler = this;
241 args.uri = uri;
242 args.cookie = cookie;
243 args.values = values;
244 args.selection = selection;
245 args.selectionArgs = selectionArgs;
246 msg.obj = args;
247
248 mWorkerThreadHandler.sendMessage(msg);
249 }
250
251 /**
252 * This method begins an asynchronous delete. When the delete operation is
253 * done {@link #onDeleteComplete} is called.
254 *
255 * @param token A token passed into {@link #onDeleteComplete} to identify
256 * the delete operation.
257 * @param cookie An object that gets passed into {@link #onDeleteComplete}
258 * @param uri the Uri passed to the delete operation.
259 * @param selection the where clause.
260 */
261 public final void startDelete(int token, Object cookie, Uri uri,
262 String selection, String[] selectionArgs) {
263 // Use the token as what so cancelOperations works properly
264 Message msg = mWorkerThreadHandler.obtainMessage(token);
265 msg.arg1 = EVENT_ARG_DELETE;
266
267 WorkerArgs args = new WorkerArgs();
268 args.handler = this;
269 args.uri = uri;
270 args.cookie = cookie;
271 args.selection = selection;
272 args.selectionArgs = selectionArgs;
273 msg.obj = args;
274
275 mWorkerThreadHandler.sendMessage(msg);
276 }
277
278 /**
279 * Called when an asynchronous query is completed.
280 *
281 * @param token the token to identify the query, passed in from
Jeff Sharkeya5f743f2009-08-12 09:53:48 -0700282 * {@link #startQuery}.
283 * @param cookie the cookie object passed in from {@link #startQuery}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 * @param cursor The cursor holding the results from the query.
285 */
286 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
287 // Empty
288 }
289
290 /**
291 * Called when an asynchronous insert is completed.
292 *
293 * @param token the token to identify the query, passed in from
294 * {@link #startInsert}.
295 * @param cookie the cookie object that's passed in from
296 * {@link #startInsert}.
297 * @param uri the uri returned from the insert operation.
298 */
299 protected void onInsertComplete(int token, Object cookie, Uri uri) {
300 // Empty
301 }
302
303 /**
304 * Called when an asynchronous update is completed.
305 *
306 * @param token the token to identify the query, passed in from
307 * {@link #startUpdate}.
308 * @param cookie the cookie object that's passed in from
309 * {@link #startUpdate}.
310 * @param result the result returned from the update operation
311 */
312 protected void onUpdateComplete(int token, Object cookie, int result) {
313 // Empty
314 }
315
316 /**
317 * Called when an asynchronous delete is completed.
318 *
319 * @param token the token to identify the query, passed in from
320 * {@link #startDelete}.
321 * @param cookie the cookie object that's passed in from
322 * {@link #startDelete}.
323 * @param result the result returned from the delete operation
324 */
325 protected void onDeleteComplete(int token, Object cookie, int result) {
326 // Empty
327 }
328
329 @Override
330 public void handleMessage(Message msg) {
331 WorkerArgs args = (WorkerArgs) msg.obj;
332
333 if (localLOGV) {
334 Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what
335 + ", msg.arg1=" + msg.arg1);
336 }
337
338 int token = msg.what;
339 int event = msg.arg1;
Jeff Sharkeya5f743f2009-08-12 09:53:48 -0700340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 // pass token back to caller on each callback.
342 switch (event) {
343 case EVENT_ARG_QUERY:
344 onQueryComplete(token, args.cookie, (Cursor) args.result);
345 break;
346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 case EVENT_ARG_INSERT:
348 onInsertComplete(token, args.cookie, (Uri) args.result);
349 break;
350
351 case EVENT_ARG_UPDATE:
352 onUpdateComplete(token, args.cookie, (Integer) args.result);
353 break;
354
355 case EVENT_ARG_DELETE:
356 onDeleteComplete(token, args.cookie, (Integer) args.result);
357 break;
358 }
359 }
360}