java|Android - NDK - sqlite

(开发历程:关于AS开发NDK的博客并不多,资源也比较少。对于刚开始接触相关知识也是摸了很多次坑,自己慢慢摸索出来)
数据库的简单操作 1. 开发需要
  • libsqliteX.so
  • classes.jar
  • sqlite3.h
官方下载
提供下载
java|Android - NDK - sqlite
文章图片

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

java|Android - NDK - sqlite
文章图片

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'] } }

java|Android - NDK - sqlite
文章图片

java|Android - NDK - sqlite
文章图片

4. native-lib.cpp
#include "sqlite3.h" #include .h> #include .h> #include "android/log.h" #defineLOG"JNILOG" #defineLOGD(...)__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 #include > #include "sqlite3.h" #include .h> #include .h> #include "android/log.h" #defineLOG"JNILOG" #defineLOGD(...)__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()); }

java|Android - NDK - sqlite
文章图片

5. Java 【java|Android - NDK - sqlite】需要添加权限
其他地方默认就好了,这里名称需要更改一下。
跟 CMakeLists.txt配置里面的 add_library里名称需要一致。
static { System.loadLibrary("sql"); }

java|Android - NDK - sqlite
文章图片

6. 注意
如果运行后,出现找不到libsqliteX.so这个库的错误信息,就在这里的libs文件下也放一个so库
java|Android - NDK - sqlite
文章图片

java|Android - NDK - sqlite
文章图片

7. 生成db文件 java|Android - NDK - sqlite
文章图片

java|Android - NDK - sqlite
文章图片

8. 查看db文件 可查看db文件的软件
java|Android - NDK - sqlite
文章图片

java|Android - NDK - sqlite
文章图片

9. 增加一些操作
  • 更新数据(name = two的 isExistence更改为“no”)
java|Android - NDK - sqlite
文章图片

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);

查看打印信息如下:
java|Android - NDK - sqlite
文章图片

  • 查询数据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 );

查看打印信息如下:
java|Android - NDK - sqlite
文章图片

  • 筛选数据
//筛选出 isExistence = yes LOGD("------------筛选数据--------------"); char* data = "https://www.it610.com/article/----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); }

查看打印信息如下:
java|Android - NDK - sqlite
文章图片

  • 删除表
运行之后,照样会生成一个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); }

java|Android - NDK - sqlite
文章图片

疑惑 问题:运行第一遍没问题,第二遍就出现这样的错误,虽然解决了,但不知道具体原因。
解决:重新再拷贝一个libsqliteX.so ,替换掉原来的那个。
java|Android - NDK - sqlite
文章图片

    推荐阅读