当前位置:首页 > 安卓源码 > 技术博客 >

从Android资源中读取SQLite数据库的方法

时间:2018-08-08 23:20 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

演示一种操作位于资产文件夹中的SQLite数据库的方法 介绍 从Android Asset Resource改进SQLite文件处理性能 背景 Android SQLiteDatabase Android资产处理 SQLite VFS SQLite URI 使用代码 一般来说,如果我们想从Android资产文件中读取SQLite数据库,我们必 演示一种操作位于资产文件夹中的SQLite数据库的方法

介绍

从Android Asset Resource改进SQLite文件处理性能

背景

  1. Android SQLiteDatabase
  2. Android资产处理
  3. SQLite VFS
  4. SQLite URI

使用代码

一般来说,如果我们想从Android资产文件中读取SQLite数据库,我们必须将资产文件复制到本地文件夹,然后从本地文件中读取数据库。

这种方法有一些缺点:

  1. 如果数据库文件大小有点大,则会浪费磁盘使用量
  2. 浪费CPU
  3. 安全易受攻击的
    用户可以在root设备之后替换本地数据库文件

为了克服这些缺点,我们将推出支持Android资源资源的新SQLite VFS。

SQLite在Unix OS上使用unix-vfs并在Window OS上使用win32-vfs

Android包装SQLite代码并导出一些java接口(android.database.sqlite.SQLiteDatabase),但遗漏了原始SQLite实现的一些高级功能

  1. 自定义功能
  2. 加密
  3. URI文件语法
  4. VFS
实际上Android已经在libsqlite.so中包含了上述功能,但是没有提供Java接口/入口点,并且根据Android O +安全行为的变化,开发人员不得在后一个Android版本中访问libsqlite.so

幸运的是,SQLite开发人员已经提供了类似的Java包装器:SQLite Android Bindings,它可以提供这些功能

类名和成员名大多相同,因此您可以导入aar并更改您的java源导入

import android.database.sqlite.SQLiteDatabase;

import org.sqlite.database.sqlite.SQLiteDatabase;

使用步骤

  1. 禁用
     build.gradle 中android资产的SQLiteDatabase文件的压缩
    aaptOptions {
        noCompress 'db'
    }
  2. 实现SQLite VFS并注册它
    sqlite3_vfs_register(&AndroidAsset::vfs, false);
    sqlite3_vfs_register(&AssetFDMap::vfs, false);
    sqlite3_vfs_register(&AssetFD::vfs, false);
  3. 使用custome URI打开asset文件夹中的数据库文件 

我为不同的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内部数据处理,而不是文件处理

从Android资源中读取SQLite数据库的方法
从Android资源中读取SQLite数据库的方法 转载https://www.codesocang.com/appboke/38808.html

技术博客阅读排行

最新文章