Android自定义数据库异步操作
2016-04-07 11:21
507 查看
平时我们在开发Android应用的时候,数据库操作是无法避免的,之前开发过程中,一直没太去在意在主线程中操作数据库,毕竟一般的数据库操作都在毫秒级的,但是考虑到极限的情况下,万一数据库要插入上万条数据呢,这样就会卡死主线程,导致应用使用不流畅,用户体验很差。
为了解决这个问题,我找了很多开源的数据库开源框架,比如OrmLite什么的,虽然这些框架都很好用,也很高大上,用了什么映射等等,Api也很简单,但是我觉得作为程序猿,一直使用别人写好的框架,语言也不是原生的数据库操作语言,那如果你用习惯之后,回头想想,你还会随便写出数据库的操作函数吗?
项目上正好有时间优化,我就去研究了一下异步数据库,我发现Google有一个AsyncQueryHandler这个类,主要用来查询系统级的数据,因为它需要确定的Uri,通过它,我写了一个关于本地数据库异步操作的类,可以给大家分享下。
如上,即为主要的异步类,主要是先通过一个Handler去操作数据库,然后用另一个Handler去回调给调用者,注意其中的Looper不是主线程的消息队列,下面是异步操作的回调,用于通知发起数据库操作的地方这次数据库操作的结果:
使用也是很简单的,在项目上,在自定义的DataBaseOpenHelper中初始化的时候初始化一下,然后实现下上面的接口,就可以通过这个DataBaseOpenHelper区调用方法异步操作数据库,这样就不会影响主线程的操作,大家可以使用StrictMode去检测下。
下面是我自定义的异步数据库操作,欢迎大家看看和提出问题:https://github.com/jianglei199212/AsyncSqlite
为了解决这个问题,我找了很多开源的数据库开源框架,比如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
相关文章推荐
- android webview js和原生态交互
- Android 中app加固与瘦身
- zz:初学android所必需的的知道的android新闻系统程序基本结构
- Android问题:ScrollView默认位置不是最顶部最全解决方案
- android studio改变主题字体大小
- android自定义view-打造圆形ImageView(三)
- Android NDK 项目依赖简单示例
- android rgb颜色渐变
- Android四大组件之Broadcast Receiver
- android关于监听电话拨打后,对方接听的状态(COLP被连接线识别提示)
- Ubuntu Android Studio 报错unable to run mksdcard sdk
- Android中自定义SwipeRefreshLayout的实现
- Android开发之EditText属性详解
- Android官方开发文档Training系列课程中文版:分享文件之配置文件共享
- Android Studio->VersionControl GitHub无法登录
- android多媒体开发笔记
- android wifi组播发送开发日常问题汇总
- android 登陆、提交数据或加载数据时提示页面
- Mac版 Android Studio快捷键
- Android中的缩略图加载-不浪费一点多余的内存