(开发历程:关于AS开发NDK的博客并不多,资源也比较少。对于刚开始接触相关知识也是摸了很多次坑,自己慢慢摸索出来)数据库的简单操作 1. 开发需要
- libsqliteX.so
- classes.jar
- sqlite3.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 .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());
}
文章图片
5. Java 【java|Android - NDK - sqlite】需要添加权限
其他地方默认就好了,这里名称需要更改一下。
跟 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 = "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);
}
查看打印信息如下:
文章图片
- 删除表
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 ,替换掉原来的那个。
文章图片
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 数据库|SQL行转列方式优化查询性能实践
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- mysql|一文深入理解mysql
- 达梦数据库|DM8表空间备份恢复