(开发历程:关于AS开发NDK的博客并不多,资源也比较少。对于刚开始接触相关知识也是摸了很多次坑,自己慢慢摸索出来)
官方下载
提供下载
* 这里没有其他需求的话就一直next,最后finish
* 之后导入class.jar包及相关so库,把sqlite3.h(头文件)直接复制在.cpp文件的同一级目录。
如何导入jar包
下图为整个工程目录:
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']
}
}
#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());
}
需要添加权限
其他地方默认就好了,这里名称需要更改一下。
跟 CMakeLists.txt配置里面的 add_library里名称需要一致。
static {
System.loadLibrary("sql");
}
如果运行后,出现找不到libsqliteX.so这个库的错误信息,就在这里的libs文件下也放一个so库
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);
}
/*
* 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);
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 ,替换掉原来的那个。