平时我们在开发Android应用的时候,数据库操作是无法避免的,之前开发过程中,一直没太去在意在主线程中操作数据库,毕竟一般的数据库操作都在毫秒级的,但是考虑到极限的情况下,万一数据库要插入上万条数据呢,这样就会卡死主线程,导致应用使用不流畅,用户体验很差。
为了解决这个问题,我找了很多开源的数据库开源框架,比如OrmLite什么的,虽然这些框架都很好用,也很高大上,用了什么映射等等,Api也很简单,但是我觉得作为程序猿,一直使用别人写好的框架,语言也不是原生的数据库操作语言,那如果你用习惯之后,回头想想,你还会随便写出数据库的操作函数吗?
项目上正好有时间优化,我就去研究了一下异步数据库,我发现Google有一个AsyncQueryHandler这个类,主要用来查询系统级的数据,因为它需要确定的Uri,通过它,我写了一个关于本地数据库异步操作的类,可以给大家分享下。
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import com.example.jianglei.asyncsqlite.Logger;
import java.util.List;
/** * 数据库异步框架 * Created by jianglei on 2016/4/6. */
public class AsyncHandler extends Handler {
private static final String TAG = "MasAsyncQueryHandler";
public static final long SUCCESS = 1;
public static final long FAIL = -1;
private static final int EVENT_ARG_SINGLE_INSERT = 0;
private static final int EVENT_ARG_MULTI_INSERT = 1;
private static final int EVENT_ARG_QUERY = 2;
private static final int EVENT_ARG_UPDATE = 3;
private static final int EVENT_ARG_DELETE = 4;
private static final int EVENT_INIT_DATABASE = 5;
private static Looper sLooper = null;
private Handler mWorkerThreadHandler;
private IAsyncHandlerCallback mIAsyncHandlerCallback;
protected static class SqliteArgs {
public SQLiteDatabase db;
public String table;
public Handler handler;
public IAsyncHandlerCallback callback;
}
protected static final class InsertSingleArgs extends SqliteArgs {
public String nullColumnHack;
public ContentValues values;
public long result;
}
protected static final class InsertMultiArgs extends SqliteArgs {
public String nullColumnHack;
public List<ContentValues> valuesList;
public long result;
}
protected static final class QueryArgs extends SqliteArgs {
public boolean distinct;
public String[] columns;
public String whereClause;
public String[] whereArgs;
public String groupBy;
public String having;
public String orderBy;
public String limit;
public Cursor result;
}
protected static final class UpdateArgs extends SqliteArgs {
public ContentValues values;
public String whereClause;
public String[] whereArgs;
public long result;
}
protected static final class DeleteArgs extends SqliteArgs {
public String whereClause;
public String[] whereArgs;
public long result;
}
protected static final class InitArgs extends SqliteArgs {
public DataBase dbOpenHelper;
public SQLiteDatabase result;
}
public AsyncHandler() {
super();
synchronized (AsyncHandler.class) {
if (sLooper == null) {
HandlerThread thread = new HandlerThread(TAG);
thread.start();
sLooper = thread.getLooper();
}
}
mWorkerThreadHandler = new WorkerHandler(sLooper);
}
/** * 初始化数据库 * * @param token 插入数据库标识 * @param dbOpenHelper 数据库 */
public void initDataBase(int token, DataBase dbOpenHelper, IInitDatabaseCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_INIT_DATABASE;
InitArgs args = new InitArgs();
args.handler = this;
args.dbOpenHelper = dbOpenHelper;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 单条插入数据库 * * @param token 插入数据库标识 * @param db 数据库对象 * @param table 数据库表名 * @param nullColumnHack 当values为空时设置的空列数据 * @param values 插入数据库内容 */
public void startSingleInsert(int token, SQLiteDatabase db, String table, String nullColumnHack, ContentValues values, ISingleInsertCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_SINGLE_INSERT;
InsertSingleArgs args = new InsertSingleArgs();
args.handler = this;
args.db = db;
args.table = table;
args.nullColumnHack = nullColumnHack;
args.values = values;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 多条插入数据库 * * @param token 插入数据库标识 * @param db 数据库对象 * @param table 数据库表名 * @param nullColumnHack 当values为空时设置的空列数据 * @param valuesList 插入数据库内容 */
public void startMultiInsert(int token, SQLiteDatabase db, String table, String nullColumnHack, List<ContentValues> valuesList, IMultiInsertCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_MULTI_INSERT;
InsertMultiArgs args = new InsertMultiArgs();
args.handler = this;
args.db = db;
args.table = table;
args.nullColumnHack = nullColumnHack;
args.valuesList = valuesList;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 查询数据库 * * @param token 插入数据库标识 * @param db 数据库对象 * @param table 数据库表名 * @param distinct 是否去除重复项 * @param columns 查询的列数组 * @param whereClause 条件 * @param whereArgs 条件参数数组 * @param groupBy 分组依据 * @param having 过滤 * @param orderBy 排序 * @param limit 限制条数 */
public void startQuery(int token, SQLiteDatabase db, boolean distinct, String table, String[] columns, String whereClause, String[] whereArgs,
String groupBy, String having, String orderBy, String limit, IQueryCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_QUERY;
QueryArgs args = new QueryArgs();
args.handler = this;
args.db = db;
args.distinct = distinct;
args.table = table;
args.columns = columns;
args.whereClause = whereClause;
args.whereArgs = whereArgs;
args.groupBy = groupBy;
args.having = having;
args.orderBy = orderBy;
args.limit = limit;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 更新数据库 * * @param token 插入数据库标识 * @param db 数据库对象 * @param table 数据库表名 * @param values 更新的内容 * @param whereClause 条件 * @param whereArgs 条件参数数组 */
public void startUpdate(int token, SQLiteDatabase db, String table, ContentValues values, String whereClause, String[] whereArgs, IUpdateCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_UPDATE;
UpdateArgs args = new UpdateArgs();
args.handler = this;
args.db = db;
args.table = table;
args.values = values;
args.whereClause = whereClause;
args.whereArgs = whereArgs;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 删除数据库某条 * * @param token 插入数据库标识 * @param db 数据库对象 * @param table 数据库表名 * @param whereClause 条件 * @param whereArgs 条件参数数组 */
public void startDelete(int token, SQLiteDatabase db, String table, String whereClause, String[] whereArgs, IDeleteCallback callback) {
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_DELETE;
DeleteArgs args = new DeleteArgs();
args.handler = this;
args.db = db;
args.table = table;
args.whereClause = whereClause;
args.whereArgs = whereArgs;
args.callback = callback;
msg.obj = args;
mWorkerThreadHandler.sendMessage(msg);
}
/** * 数据库处理 */
protected class WorkerHandler extends Handler {
public WorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int token = msg.what;
int event = msg.arg1;
InsertSingleArgs insertSingleArgs;
InsertMultiArgs insertMultiArgs;
QueryArgs queryArgs;
UpdateArgs updateArgs;
DeleteArgs deleteArgs;
InitArgs initArgs;
Message reply;
switch (event) {
case EVENT_ARG_SINGLE_INSERT:
insertSingleArgs = (InsertSingleArgs) msg.obj;
insertSingleArgs.result = insertSingleArgs.db.insertOrThrow(insertSingleArgs.table,
insertSingleArgs.nullColumnHack, insertSingleArgs.values);
if ((int) insertSingleArgs.result == -1) {
Logger.e(TAG + " ---->> insert single args failed!");
insertSingleArgs.result = FAIL;
} else {
insertSingleArgs.result = SUCCESS;
}
reply = insertSingleArgs.handler.obtainMessage(token);
reply.obj = insertSingleArgs;
break;
case EVENT_ARG_MULTI_INSERT:
insertMultiArgs = (InsertMultiArgs) msg.obj;
insertMultiArgs.db.beginTransaction();
for (ContentValues values : insertMultiArgs.valuesList) {
insertMultiArgs.result = insertMultiArgs.db.insertOrThrow(insertMultiArgs.table,
insertMultiArgs.nullColumnHack, values);
if ((int) insertMultiArgs.result == -1) {
Logger.e(TAG + " ---->> insert multi args failed!");
insertMultiArgs.result = FAIL;
break;
} else {
insertMultiArgs.result = SUCCESS;
}
}
insertMultiArgs.db.setTransactionSuccessful();
insertMultiArgs.db.endTransaction();
reply = insertMultiArgs.handler.obtainMessage(token);
reply.obj = insertMultiArgs;
break;
case EVENT_ARG_QUERY:
queryArgs = (QueryArgs) msg.obj;
Cursor cursor;
try {
cursor = queryArgs.db.query(queryArgs.distinct, queryArgs.table,
queryArgs.columns, queryArgs.whereClause, queryArgs.whereArgs,
queryArgs.groupBy, queryArgs.having, queryArgs.orderBy, queryArgs.limit);
// 调用这个方法会使得主线程小小的加速,此处先遍历cursor,之后在主线程中去遍历取值时会加速,好像是这样的
if (cursor != null) {
cursor.getCount();
}
} catch (Exception e) {
Logger.e(TAG + " ---->> Exception thrown during handling EVENT_ARG_QUERY", e);
cursor = null;
}
queryArgs.result = cursor;
reply = queryArgs.handler.obtainMessage(token);
reply.obj = queryArgs;
break;
case EVENT_ARG_UPDATE:
updateArgs = (UpdateArgs) msg.obj;
updateArgs.result = updateArgs.db.update(updateArgs.table, updateArgs.values, updateArgs.whereClause, updateArgs.whereArgs);
if ((int) updateArgs.result <= 0) {
Logger.e(TAG + " ---->> update args failed!");
updateArgs.result = FAIL;
} else {
updateArgs.result = SUCCESS;
}
reply = updateArgs.handler.obtainMessage(token);
reply.obj = updateArgs;
break;
case EVENT_ARG_DELETE:
deleteArgs = (DeleteArgs) msg.obj;
deleteArgs.result = deleteArgs.db.delete(deleteArgs.table, deleteArgs.whereClause, deleteArgs.whereArgs);
if ((int) deleteArgs.result <= 0) {
Logger.e(TAG + " ---->> delete args failed!");
deleteArgs.result = FAIL;
} else {
deleteArgs.result = SUCCESS;
}
reply = deleteArgs.handler.obtainMessage(token);
reply.obj = deleteArgs;
break;
case EVENT_INIT_DATABASE:
initArgs = (InitArgs) msg.obj;
initArgs.result = initArgs.dbOpenHelper.getWritableDatabase();
reply = initArgs.handler.obtainMessage(token);
reply.obj = initArgs;
break;
default:
return;
}
reply.arg1 = msg.arg1;
reply.sendToTarget();
}
}
/** * 数据库处理结果回调 */
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int token = msg.what;
int event = msg.arg1;
switch (event) {
case EVENT_ARG_SINGLE_INSERT:
InsertSingleArgs insertSingleArgs = (InsertSingleArgs) msg.obj;
if (insertSingleArgs.callback != null) {
if (insertSingleArgs.result == SUCCESS) {
((ISingleInsertCallback) insertSingleArgs.callback).onSingleInsertComplete(token, insertSingleArgs.result);
} else {
insertSingleArgs.callback.onAsyncOperateFailed();
}
}
break;
case EVENT_ARG_MULTI_INSERT:
InsertMultiArgs insertMultiArgs = (InsertMultiArgs) msg.obj;
if (insertMultiArgs.callback != null) {
if (insertMultiArgs.result ==SUCCESS) {
((IMultiInsertCallback) insertMultiArgs.callback).onMultiInsertComplete(token, insertMultiArgs.result);
} else {
insertMultiArgs.callback.onAsyncOperateFailed();
}
}
break;
case EVENT_ARG_QUERY:
QueryArgs queryArgs = (QueryArgs) msg.obj;
if (queryArgs.callback != null) {
if (queryArgs.result != null) {
((IQueryCallback) queryArgs.callback).onQueryComplete(token, queryArgs.result);
} else {
queryArgs.callback.onAsyncOperateFailed();
}
}
break;
case EVENT_ARG_UPDATE:
UpdateArgs updateArgs = (UpdateArgs) msg.obj;
if (updateArgs.callback != null) {
if (updateArgs.result == SUCCESS) {
((IUpdateCallback) updateArgs.callback).onUpdateComplete(token, updateArgs.result);
} else {
updateArgs.callback.onAsyncOperateFailed();
}
}
break;
case EVENT_ARG_DELETE:
DeleteArgs deleteArgs = (DeleteArgs) msg.obj;
if (deleteArgs.callback != null) {
if (deleteArgs.result == SUCCESS) {
((IDeleteCallback) deleteArgs.callback).onDeleteComplete(token, deleteArgs.result);
} else {
deleteArgs.callback.onAsyncOperateFailed();
}
}
break;
case EVENT_INIT_DATABASE:
InitArgs initArgs = (InitArgs) msg.obj;
if (initArgs.callback != null) {
if (initArgs.result != null) {
((IInitDatabaseCallback) initArgs.callback).onInitDatabaseComplete(token, initArgs.result);
} else {
initArgs.callback.onAsyncOperateFailed();
}
}
break;
default:
return;
}
}
}
如上,即为主要的异步类,主要是先通过一个Handler去操作数据库,然后用另一个Handler去回调给调用者,注意其中的Looper不是主线程的消息队列,下面是异步操作的回调,用于通知发起数据库操作的地方这次数据库操作的结果:
/** * 数据库异步操作结果回调基类 * Created by jianglei on 2016/4/6. */
public interface IAsyncHandlerCallback {
void onAsyncOperateFailed();
}
public interface IInitDatabaseCallback extends IAsyncHandlerCallback {
/** * 初始化成功 */
void onInitDatabaseComplete(int token, SQLiteDatabase db);
}
public interface ISingleInsertCallback extends IAsyncHandlerCallback {
/** * 单条插入成功 */
void onSingleInsertComplete(int token, long result);
}
public interface IMultiInsertCallback extends IAsyncHandlerCallback {
/** * 多条插入成功 */
void onMultiInsertComplete(int token, long result);
}
public interface IQueryCallback extends IAsyncHandlerCallback{
/** * 查询成功 */
void onQueryComplete(int token, Cursor cursor);
}
public interface IUpdateCallback extends IAsyncHandlerCallback {
/** * 更新成功 */
void onUpdateComplete(int token, long result);
}
public interface IDeleteCallback extends IAsyncHandlerCallback {
/** * 删除成功 */
void onDeleteComplete(int token, long result);
}
使用也是很简单的,在项目上,在自定义的DataBaseOpenHelper中初始化的时候初始化一下,然后实现下上面的接口,就可以通过这个DataBaseOpenHelper区调用方法异步操作数据库,这样就不会影响主线程的操作,大家可以使用StrictMode去检测下。
下面是我自定义的异步数据库操作,欢迎大家看看和提出问题:https://github.com/jianglei199212/AsyncSqlite