Android - NDK - sqlite

    技术2025-03-23  22

    (开发历程:关于AS开发NDK的博客并不多,资源也比较少。对于刚开始接触相关知识也是摸了很多次坑,自己慢慢摸索出来)

    数据库的简单操作

    1. 开发需要

    libsqliteX.soclasses.jarsqlite3.h

    官方下载 提供下载

    2. 新建 Native C++工程

    * 这里没有其他需求的话就一直next,最后finish * 之后导入class.jar包及相关so库,把sqlite3.h(头文件)直接复制在.cpp文件的同一级目录。 如何导入jar包 下图为整个工程目录:

    3. 配置

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.4.1) add_library(sql SHARED native-lib.cpp ) add_library(sqliteX SHARED IMPORTED ) set_target_properties( sqliteX PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/arm64-v8a/libsqliteX.so) find_library(log-lib log ) target_link_libraries(sql sqliteX ${log-lib} )

    build.gradle

    //----------添加1-------------- ndk{ abiFilters 'arm64-v8a' } //-----------添加2-------------- sourceSets { main { jniLibs.srcDirs = ['libs'] } }

    4. native-lib.cpp

    #include "sqlite3.h" #include <stdio.h> #include <stdlib.h> #include "android/log.h" #define LOG "JNILOG" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__) sqlite3 * db; char * pErrMsg = 0; int ret = 0; char* sql; sqlite3_stmt * stmt; 打开数据库 //数据库存放的位置(文件名) const char *filename = "storage//emulated//0//1//myText.db"; /* * SQLITE_API int sqlite3_open(const char *filename, sqlite3 **ppDb); * 参数1: 数据库存放的位置(文件名) * 参数2: 关键数据类型(默认:sqlite3 *,不用管) * * 如果正常打开,ret为 0(SQLITE_OK) * 如果打开失败,ret为其他数 * 文件名不需要一定存在,如果此文件不存在,sqlite 会自动建立它。 * 如果它存在,就尝试把它当数据库文件来打开。 * */ LOGD("------------打开数据库--------------"); ret = sqlite3_open(filename, &db); if ( ret != SQLITE_OK ) { LOGD("打开失败失败,错误码:%d------错误原因:%s",ret,sqlite3_errmsg(db)); return(-1); } 创建表格 //创建一个表名叫:MyTet //有三个字段:id、name、isExistence sql = "create table myText(" \ "id int primary key not null," \ "name text not null," \ "isExistence char(4) not null);"; /* * 执行建表SQL * int sqlite3_exec(sqlite3*, const char *sql, sqlite3_callback, void *, char **errmsg ); * * 参数1:关键数据 * 参数2:sql语句,以/0结尾 * 参数3:回调,当这条语句执行之后,sqlite3会去调用你提供的这个函数 * 参数4:void * 是你所提供的指针,你可以传递任何一个指针参数到这里, * 这个参数最终会传到回调函数里面,如果不需要传递指针给回调函数,可以填NULL * 参数5:错误信息 * */ LOGD("------------建表--------------"); ret = sqlite3_exec( db, sql, 0, 0, &pErrMsg ); if ( ret != SQLITE_OK ) { LOGD("创建表失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); sqlite3_free(pErrMsg); } 插入数据 //要插入的数据 sql = "insert into myText (id, name, isExistence)" \ "values (001, 'one', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (002, 'two', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (003, 'three', 'no');" \ "insert into myText (id, name, isExistence)" \ "values (004, 'four', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (005, 'five', 'no');"; // 执行插入记录 LOGD("------------插入数据--------------"); ret = sqlite3_exec( db, sql, 0, 0, &pErrMsg); if ( ret != SQLITE_OK ) { LOGD("插入失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); } 取数据 //取数据 LOGD("------------取数据--------------"); //限制返回2行数据,且从第3行数据开始 sql = "select * from myText limit 2 offset 3;"; sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL); LOGD("----------------total_column = %d-------------", sqlite3_column_count(stmt)); /* 遍历执行sql语句后的结果集的每一行数据 */ while(sqlite3_step(stmt) == SQLITE_ROW){ LOGD("-------------------------"); /* 获得字节数,第二个参数为select结果集合的列号 */ /* 由于select 的结果集只有section这一列,因此为0 */ int len_id = sqlite3_column_bytes(stmt, 0); int len_name = sqlite3_column_bytes(stmt, 1); int len_isExistence = sqlite3_column_bytes(stmt, 2); LOGD("------------id = %d, len = %d-------------", sqlite3_column_int(stmt, 0), len_id); LOGD("------------name = %s, len = %d------------", sqlite3_column_text(stmt, 1), len_name); LOGD("------------isExistence = %s, len = %d--------", sqlite3_column_text(stmt, 2), len_isExistence ); } sqlite3_finalize(stmt); 关闭数据库 sqlite3_close(db); db = 0; LOGD("------------关闭数据库--------------"); 完整代码 #include <jni.h> #include <string> #include "sqlite3.h" #include <stdio.h> #include <stdlib.h> #include "android/log.h" #define LOG "JNILOG" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__) int sql(){ sqlite3 * db; char * pErrMsg = 0; int ret = 0; char* sql; sqlite3_stmt * stmt; const char *filename = "storage//emulated//0//1//myText.db"; LOGD("------------打开数据库--------------"); ret = sqlite3_open(filename, &db); if ( ret != SQLITE_OK ) { LOGD("打开失败失败,错误码:%d------错误原因:%s",ret,sqlite3_errmsg(db)); return(-1); } sql = "create table myText(" \ "id int primary key not null," \ "name text not null," \ "isExistence char(4) not null);"; LOGD("------------建表--------------"); ret = sqlite3_exec( db, sql, 0, 0, &pErrMsg ); if ( ret != SQLITE_OK ) { LOGD("创建表失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); sqlite3_free(pErrMsg); } sql = "insert into myText (id, name, isExistence)" \ "values (001, 'one', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (002, 'two', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (003, 'three', 'no');" \ "insert into myText (id, name, isExistence)" \ "values (004, 'four', 'yes');" \ "insert into myText (id, name, isExistence)" \ "values (005, 'five', 'no');"; LOGD("------------插入数据--------------"); ret = sqlite3_exec( db, sql, 0, 0, &pErrMsg); if ( ret != SQLITE_OK ) { LOGD("插入失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); } //取数据 LOGD("------------取数据--------------"); //限制返回2行数据,且从第3行数据开始 sql = "select * from myText limit 2 offset 3;"; sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL); LOGD("----------------total_column = %d-------------", sqlite3_column_count(stmt)); /* 遍历执行sql语句后的结果集的每一行数据 */ while(sqlite3_step(stmt) == SQLITE_ROW){ LOGD("-------------------------"); /* 获得字节数,第二个参数为select结果集合的列号 */ /* 由于select 的结果集只有section这一列,因此为0 */ int len_id = sqlite3_column_bytes(stmt, 0); int len_name = sqlite3_column_bytes(stmt, 1); int len_isExistence = sqlite3_column_bytes(stmt, 2); LOGD("------------id = %d, len = %d-------------", sqlite3_column_int(stmt, 0), len_id); LOGD("------------name = %s, len = %d------------", sqlite3_column_text(stmt, 1), len_name); LOGD("------------isExistence = %s, len = %d--------", sqlite3_column_text(stmt, 2), len_isExistence ); } sqlite3_finalize(stmt); sqlite3_close(db); db = 0; LOGD("------------关闭数据库--------------"); return 0; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_jni_1jql_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; sql(); return env->NewStringUTF(hello.c_str()); }

    5. Java

    需要添加权限 其他地方默认就好了,这里名称需要更改一下。 跟 CMakeLists.txt配置里面的 add_library里名称需要一致。

    static { System.loadLibrary("sql"); }

    6. 注意

    如果运行后,出现找不到libsqliteX.so这个库的错误信息,就在这里的libs文件下也放一个so库

    7. 生成db文件

    8. 查看db文件

    可查看db文件的软件

    9. 增加一些操作

    更新数据(name = two的 isExistence更改为“no”)

    LOGD("------------更新数据--------------"); char* data1 = "update call back function call!\n"; // 把数据库里原本名为 two,isExistence = 'yes' // 更改为 isExistence = 'no' sql = "update myText set isExistence = 'no' where name='two';" \ "select * from myText where isExistence == 'yes'"; ret = sqlite3_exec(db, sql, 0, data1, &pErrMsg); if( ret != SQLITE_OK ){ LOGD("更新失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); sqlite3_free(&pErrMsg); } 查询数据1(使用回调方法) /* * typedef int (*sqlite3_callback)(void* para,int n_column,char** column_value, char** column_name ); * 参数1:默认 * 参数2:记录有多少个字段(即这条记录有多少列) * 参数3:关键值,查出来的数据都保存在这里,它实际上是个1维数组(不要以为是2维数组), * 每一个元素都是一个 char * 值,是一个字段内容(用字符串来表示,以/0结尾) * 参数4:跟参数3对应,表示这个字段的字段名称 * */ static int _sql_callback(void * notused, int argc, char ** argv, char ** szColName) { int i; LOGD("------------记录包含 %d 个字段--------------",argc); for ( i=0; i < argc; i++ ) { LOGD( "--字段名:%s ---- 字段值:%s--", szColName[i], argv[i] ); } return 0; } const char * sql1 = "select * from myText"; LOGD("------------查询数据--------------"); // 使用回调方法,查询数据表 sqlite3_exec( db, sql1, _sql_callback, 0, &pErrMsg);

    查看打印信息如下:

    查询数据2(不使用回调方法) char * errmsg = NULL; char **dbResult; int nRow, nColumn; int i , j; int index; ret = sqlite3_get_table( db, "select * from myText", &dbResult, &nRow, &nColumn, &errmsg ); if( SQLITE_OK == ret ) { //查询成功 //从 nColumn索引开始才是真正的数据 index = nColumn; LOGD("---查到%d条记录/n", nRow); for( i = 0; i < nRow ; i++ ) { LOGD("---第 %d 条记录---/n", i+1 ); for( j = 0 ; j < nColumn; j++ ) { LOGD("字段名:%s --- 字段值:%s/n", dbResult[j], dbResult [index]); ++index; } } } //到这里,不论数据库查询是否成功,都释放 char** 查询结果,使用 sqlite 提供的功能来释放 sqlite3_free_table( dbResult );

    查看打印信息如下:

    筛选数据 //筛选出 isExistence = yes LOGD("------------筛选数据--------------"); char* data = "----select call back function call!\n"; sql = "select * from myText where isExistence == 'yes';"; ret = sqlite3_exec(db, sql, _sql_callback, data, &pErrMsg); if( ret != SQLITE_OK ){ LOGD("筛选失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); sqlite3_free(&pErrMsg); }

    查看打印信息如下:

    删除表

    运行之后,照样会生成一个db文件。但打开这个文件,里面什么都没有。

    LOGD("------------删除表--------------"); sql = "drop table myText;"; ret = sqlite3_exec(db, sql, NULL, NULL, &pErrMsg); if( ret != SQLITE_OK ){ LOGD("删除失败,错误码:%d--------错误原因:%s",ret,sqlite3_errmsg(db)); sqlite3_free(&pErrMsg); }

    疑惑

    问题:运行第一遍没问题,第二遍就出现这样的错误,虽然解决了,但不知道具体原因。 解决:重新再拷贝一个libsqliteX.so ,替换掉原来的那个。

    Processed: 0.009, SQL: 9