ContentProvider中实现了常见的insert,query,delete和update的方法就能完成基本操作了,可是遇到批量的操作怎么办?可以用循环一条条的插入,当然也可以使用ContentProvider的如下两个方法:
public ContentProviderResult[] applyBatch(ArrayList operations)
throws OperationApplicationException {
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
results[i] = operations.get(i).apply(this, results, i);
}
return results;
}
public int bulkInsert(Uri uri, ContentValues[] values) {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
insert(uri, values[i]);
}
return numValues;
}
上述是默认实现,可以看出实际上还是循环实现,和你自己写循环没啥区别。那么批量操作如何优化效率,从最近遇到的一个bug讲起。
最近一个app批量操作问题相当严重,插入500多条数据要耗时86秒,真是跌破眼镜。开始尝试使用applyBatch方法,可是如上分析是不会有什么效果的,日志分析发现多次引发数据库监听的回调和查询。
原因是insert,delete和update标准实现中,一般都会在操作完毕后发送通知,例如:
getContext().getContentResolver().notifyChange(AUTHORITY_URI, null,
false);
这样app层次会得到通知刷新页面。那么在循环中的每次操作中都会引发数据的重查,而且由于不专业的代码重查会触发多次:
在数据库操作完毕后,加入重查的代码
实际上CursorAdapter是默认会自动刷新的,只要ContentProvider中代码中有notifyChange。
三种方式代码中都有,即插一条数据会触发三次重查,太恐怖了。保留三种中的一个就可以了,是个listview列表页面的话推荐用最后一种方式。
减少了两次重查后发现,最后一次还是多余的,应该是批量插入后重查一次即可,而不是循环中的每一次都触发重查。
要想实现这个,那么必须继承修改bulkInsert和applyBatch方法,使得notify减少到最少。
批量操作加入事务,只写一次数据库,避免了多次数据库操作,见网上的诸多使用事务后的效率时间对比,使用事务可以大幅度的提高效率。默认的ContentProvider是没有事务的,要自己加上。下面是个模板代码,和业务无关,任何ContentProvider稍微改下都可以用。
private boolean mIsInTransaction = false;
public ContentProviderResult[] applyBatch(
ArrayList operations)
throws OperationApplicationException {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
db.beginTransaction();//开始事务
mIsInTransaction = true;
try{
ContentProviderResult[] results = super.applyBatch(operations);
db.setTransactionSuccessful();//设置事务标记为successful
return results;
} finally {
mIsInTransaction = false;
db.endTransaction();//结束事务
notifyChange();
}
}
protected void notifyChange() {
if(!mIsInTransaction) {
getContext().getContentResolver().notifyChange(AUTHORITY_URI, null,
false);
}
}
这个问题中,实现ContentProvider的代码中没有加入notifyChange,导致app侧有同学为了解决bug到处加入手动查询的代码,后续有人接手后又加入了数据库监听的代码,再往后ContentProvider加入了notifyChange的代码。而且app侧没有使用批量方法,数据库也没有实现批量方法。
归根到底是对数据库基本知识和Android源码的了解不足,很多问题都是开发自己造出来的。很多问题Android本身现有的框架都可以解决的,不用自己造轮子,同时造轮子的99%以上的不会比google造的好。