原文地址:http://blog.csdn.net/wcs542882916
packageandroid.content;
importandroid.database.Cursor;
importandroid.net.Uri;
importandroid.os.Handler;
importandroid.os.HandlerThread;
importandroid.os.Looper;
importandroid.os.Message;
importandroid.util.Log;
importjava.lang.ref.WeakReference;
/**
* 这个异步查询Handler的原理就是两个Handler相互发消息。
* 外部Handler#startQuery(发送消息到内部Handler),
* 内部Handler(另外一个线程)接收消息并查询数据库,
* 查询结束后,再从内部发消息到外部Handler通知查询完毕,
* 外部Handler处理消息调用Handler#onQueryComplete,至此一个回合结束。
*
* Ahelper class to help make handling asynchronous {@link ContentResolver}
*queries easier.
*
* 这是一个帮助类,使得ContentResolver异步查询更加便捷
*/
public abstract classAsyncQueryHandler extendsHandler {
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;
/*
*下面这三个成员变量都会在AsyncQueryHandler的构造方法里被初始化
*
*sLooper是另起了一个线程newHandlerThread("AsyncQueryWorker"),并获取此线程的Looper
*
*mWorkerThreadHandler使用sLooper创建的一个Handler,
*这个Handler处理的message都会在Looper所在线程里执行
*/
/* package */finalWeakReference<ContentResolver> mResolver;//弱引用,暂时不懂
private staticLooper sLooper= null;
private Handler mWorkerThreadHandler;
protected static final classWorkerArgs {
publicUri uri;
publicHandler handler;
publicString[] projection;
publicString selection;
publicString[] selectionArgs;
publicString orderBy;
publicObject result;
publicObject cookie;
publicContentValues values;
}
protected classWorkerHandler extendsHandler {
publicWorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
finalContentResolver resolver = mResolver.get();
if(resolver == null)return;
WorkerArgs args = (WorkerArgs) msg.obj;
inttoken = msg.what;
intevent = 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.
//调用getCount()方法能够引起cursor window被填充,这将使得在主线程第一次存取的更快
if (cursor != null) {
cursor.getCount();
}
} catch (Exception e) {
Log.w(TAG, "Exceptionthrown 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 tothe 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= newWeakReference<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'tgoing 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 thegiven URI.
* @param selectionArgs You may include ?s in selection, which will be
* replaced by the valuesfrom 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 theORDER BY itself). Passing null will use the
* default sort order,which may be unordered.
*
* 这个方法开始一个异步查询。当查询完成的时候#onQueryComplete方法会被调用。
* token 一个token被传入到#onQueryComplete来标识查询的发起地。
* cookie 一个对象被传入到#onQueryComplete方法内,可以是用来封装数据的对象。
* projection 获取那些列数据,传入null值将查询所有列。
* selection 获取那些行,传入null将查询所有行。
* selectionArgs 在selection参数里可以加入一些?s,查询的时候这些?s将会被selectionArgs里的值替换。
* orderBy 对行进行排序,传入null将会使用默认排序,默认排序也有可能是无序的。
*/
public voidstartQuery(inttoken, Object cookie, Uri uri,
String[] projection, String selection, String[] selectionArgs,
String orderBy) {
// Use the token as what socancelOperations works properly
//将token作为Message#what字段,所以取消操作能够起作用。
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1= EVENT_ARG_QUERY;
WorkerArgs args = newWorkerArgs();
args.handler= this;//AsyncQueryHandler
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 stillmay
* 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 thesame token they will all be canceled.
*
* 取消token所标识的Message。这里不保证取消操作会成功。
*/
public final void cancelOperation(int token) {
mWorkerThreadHandler.removeMessages(token);
}
/**
* This method begins an asynchronous insert. When the insert operationis
* 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 socancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1= EVENT_ARG_INSERT;
WorkerArgs args = newWorkerArgs();
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 operationis
* 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 socancelOperations 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 operationis
* 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 voidstartDelete(inttoken, Object cookie, Uri uri,
String selection, String[] selectionArgs) {
// Use the token as what socancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1= EVENT_ARG_DELETE;
WorkerArgs args = newWorkerArgs();
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 voidonQueryComplete(inttoken, 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 voidonInsertComplete(inttoken, 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 voidonUpdateComplete(inttoken, Object cookie, intresult) {
// 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 voidonDeleteComplete(inttoken, Object cookie, intresult) {
// Empty
}
@Override
public voidhandleMessage(Message msg) {
WorkerArgs args = (WorkerArgs) msg.obj;
if(localLOGV) {
Log.d(TAG,"AsyncQueryHandler.handleMessage: msg.what="+ msg.what
+ ", msg.arg1=" + msg.arg1);
}
inttoken = msg.what;
intevent = msg.arg1;
// pass token back to caller on eachcallback.
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;
}
}
}