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