Android手机开机过程中,MediaScanner会对手机上的文件进行扫描,然后把文件信息保存到数据库中;或者,当手机导入大量图片文件时,MediaScanner也会执行同样的操作。这过程涉及数据库的增删改查操作,我们直观地知道批量插入数据可以提高效率,但是细节是怎样的呢?所以本文主要探究批量插入数据的源码。
在MediaScanner.scanDirectories()方法中可以看到,当开启ENABLE_BULK_INSERTS进行批量插入的时候,会创建MediaInserter对象,该对象负责缓存待插入的数据,数据达到设定的bufferSize后,调用ContentProvider进行批量插入。MediaInserter使用的数据结构如下所示,比如,对于这样的Uri: “content://media/external/images/media”,它指向external.db的images 视图,如果新增并扫描出来100张图片,那么对应的List就有100条数据,MediaInserter就是这样把待插入的数据缓存起来的,下一步就是调用MediaProvider插入这100条数据。
private final HashMap<Uri, List<ContentValues>> mRowMap =
new HashMap<Uri, List<ContentValues>>();
ContentProvider是个抽象类,它的bulkInsert方法默认实现是逐条插入数据的,需要批量插入逻辑就得覆写这个方法。
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
insert(uri, values[i]); //逐条插入数据
}
return numValues;
}
所以从源码中找到MediaProvider的bulkInsert方法,如下,其实它就是开启事务、循环调用db.insert()方法、提交事务、结束事务的过程。
@Override
public int bulkInsert(Uri uri, ContentValues values[]) {
//......
synchronized (mDirectoryCache) {
db.beginTransaction();
try {
int len = values.length;
for (int i = 0; i < len; i++) {
if (values[i] != null) {
insertInternal(uri, match, values[i], notifyRowIds);
}
}
numInserted = len;
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
//......
}
为什么使用事务可以提高批量插入数据的效率呢?我想是因为减少了磁盘的写入次数。实验测试使用事务批量插入确实节省许多时间。