361 lines
13 KiB
Java
361 lines
13 KiB
Java
|
/*
|
||
|
* Copyright (C) 2007 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.content;
|
||
|
|
||
|
import android.database.Cursor;
|
||
|
import android.net.Uri;
|
||
|
import android.os.Handler;
|
||
|
import android.os.HandlerThread;
|
||
|
import android.os.Looper;
|
||
|
import android.os.Message;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import java.lang.ref.WeakReference;
|
||
|
|
||
|
/**
|
||
|
* 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<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);
|
||
|
// 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, e.toString());
|
||
|
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<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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
}
|
||
|
}
|
||
|
}
|