从Android Asset Resource改进SQLite文件处理性能
一般来说,如果我们想从Android资产文件中读取SQLite数据库,我们必须将资产文件复制到本地文件夹,然后从本地文件中读取数据库。
这种方法有一些缺点:
为了克服这些缺点,我们将推出支持Android资源资源的新SQLite VFS。
Android包装SQLite代码并导出一些java接口(android.database.sqlite.SQLiteDatabase),但遗漏了原始SQLite实现的一些高级功能
幸运的是,SQLite开发人员已经提供了类似的Java包装器:SQLite Android Bindings,它可以提供这些功能
类名和成员名大多相同,因此您可以导入aar并更改您的java源导入
import android.database.sqlite.SQLiteDatabase;
至
import org.sqlite.database.sqlite.SQLiteDatabase;
aaptOptions {
noCompress 'db'
}
sqlite3_vfs_register(&AndroidAsset::vfs, false); sqlite3_vfs_register(&AssetFDMap::vfs, false); sqlite3_vfs_register(&AssetFD::vfs, false);
我为不同的scenerio实现了三个VFS
第一个VFS:android_asset
Java(使用自定义SQLite URI打开SQLiteDatabase):
try (SQLiteDatabase db = SQLiteDatabase.openDatabase("file:asset_db.db?vfs=android_asset&immutable=1&mode=ro", null, SQLiteDatabase.OPEN_READONLY)) {
................................
}
本机:
static int xRead(sqlite3_file *file, void *buf, int iAmt, sqlite3_int64 iOfst) {
vfs_file *f = (vfs_file *) file;
int expectReadLen = (iAmt + iOfst > f->length) ? (f->length - iOfst) : iAmt;
int readLen = pread64(f->fd, buf, expectReadLen, iOfst + f->offset);
if (readLen < 0) {
return SQLITE_IOERR_READ;
} else if (readLen == expectReadLen) {
return SQLITE_OK;
} else {
memset((__uint8_t *) buf + readLen, 0, iAmt - readLen);
return SQLITE_IOERR_SHORT_READ;
}
}
static int vfsOpen(sqlite3_vfs *vfs, const char *path, sqlite3_file *file, int flags,
int *outflags) {
ALOGD("%s:: path=%s flags=%x", __FUNCTION__, path, flags);
if (g_AAssetManager == NULL) {
return SQLITE_ERROR;
}
vfs_file *f = (vfs_file *) file;
f->pMethods = &vfs_io_methods;
AAsset *asset = AAssetManager_open(g_AAssetManager, path, AASSET_MODE_RANDOM);
if (asset == NULL) { //if the asset file don't exist
return SQLITE_NOTFOUND;
}
f->fd = AAsset_openFileDescriptor64(asset, &f->offset, &f->length);
AAsset_close(asset);
if (f->fd < 0) { //if the asset file is compressed
return SQLITE_NOTFOUND;
}
*outflags = flags;
return SQLITE_OK;
}
第二个VFS:asset_fd_map
Java(使用自定义SQLite URI打开SQLiteDatabase):
try (AssetFileDescriptor afd = getAssets().openFd("asset_db.db")) {
try (SQLiteDatabase db = SQLiteDatabase.openDatabase(String.format("file:%X_%X_%X?vfs=asset_fd_map&immutable=1&mode=ro", afd.getParcelFileDescriptor().getFd(), afd.getStartOffset(), afd.getLength()), null, SQLiteDatabase.OPEN_READONLY)) {
.................................
}
}
本机:
static int xRead(sqlite3_file *file, void *buf, int iAmt, sqlite3_int64 iOfst) {
vfs_file *f = (vfs_file *) file;
int expectReadLen = (iAmt + iOfst > f->length) ? (f->length - iOfst) : iAmt;
memcpy(buf, (__uint8_t *) f->address + iOfst + f->offset, expectReadLen);
int readLen = expectReadLen;
return SQLITE_OK;
}
static int vfsOpen(sqlite3_vfs *vfs, const char *path, sqlite3_file *file, int flags,
int *outflags) {
ALOGD("%s:: path=%s flags=%x", __FUNCTION__, path, flags);
vfs_file *f = (vfs_file *) file;
f->pMethods = &vfs_io_methods;
if (3 > sscanf(path, "%x_%llx_%llx", &f->fd, &f->offsetFileStart, &f->length)) {
return SQLITE_ERROR;
}
//because mmap() require the offset must be on the page boundary
__int64_t offsetToPage = (f->offsetFileStart / 4096) * 4096;
f->offsetMapStart = f->offsetFileStart - offsetToPage;
f->mapLength = f->length + f->offsetMapStart;
f->address = mmap64(NULL, f->mapLength, PROT_READ, MAP_PRIVATE, f->fd, offsetToPage);
if (f->address == MAP_FAILED) {
return SQLITE_ERROR;
}
*outflags = flags;
return SQLITE_OK;
}
第三个VFS:asset_fd
Java(使用自定义SQLite URI打开SQLiteDatabase):
try (AssetFileDescriptor afd = getAssets().openFd("asset_db.db")) {
try (SQLiteDatabase db = SQLiteDatabase.openDatabase(String.format("file:%X_%X_%X?vfs=asset_fd&immutable=1&mode=ro", afd.getParcelFileDescriptor().getFd(), afd.getStartOffset(), afd.getLength()), null, SQLiteDatabase.OPEN_READONLY)) {
.................................
}
}
本机:
static int xRead(sqlite3_file *file, void *buf, int iAmt, sqlite3_int64 iOfst) {
vfs_file *f = (vfs_file *) file;
int expectReadLen = (iAmt + iOfst > f->length) ? (f->length - iOfst) : iAmt;
int readLen = pread64(f->fd, buf, expectReadLen, iOfst + f->offset);
if (readLen < 0) {
return SQLITE_IOERR_READ;
} else if (readLen == expectReadLen) {
return SQLITE_OK;
} else {
memset((__uint8_t *) buf + readLen, 0, iAmt - readLen);
return SQLITE_IOERR_SHORT_READ;
}
}
static int vfsOpen(sqlite3_vfs *vfs, const char *path, sqlite3_file *file, int flags,
int *outflags) {
ALOGD("%s:: path=%s flags=%x", __FUNCTION__, path, flags);
vfs_file *f = (vfs_file *) file;
f->pMethods = &vfs_io_methods;
if (3 > sscanf(path, "%x_%llx_%llx", &f->fd, &f->offset, &f->length)) {
return SQLITE_ERROR;
}
*outflags = flags;
return SQLITE_OK;
}
在演示项目中,我比较了不同的方法。
经过时间差异似乎不是太大,可能是因为性能瓶颈是SQLite内部数据处理,而不是文件处理
热门源码