Android - NDK - sqlite

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

数据库的简单操作

1. 开发需要

  • libsqliteX.so
  • classes.jar
  • sqlite3.h

官方下载
提供下载

在这里插入图片描述

2. 新建 Native C++工程

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

在这里插入图片描述

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

Android - NDK - sqlite_第3张图片
在这里插入图片描述

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库

Android - NDK - sqlite_第4张图片
在这里插入图片描述

7. 生成db文件

Android - NDK - sqlite_第5张图片
在这里插入图片描述

8. 查看db文件

可查看db文件的软件
Android - NDK - sqlite_第6张图片

在这里插入图片描述

9. 增加一些操作

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

Android - NDK - sqlite_第7张图片

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

查看打印信息如下:
Android - NDK - sqlite_第8张图片

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

查看打印信息如下:

Android - NDK - sqlite_第9张图片

  • 筛选数据
	//筛选出 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);
    }

查看打印信息如下:
Android - NDK - sqlite_第10张图片

  • 删除表

运行之后,照样会生成一个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 ,替换掉原来的那个。
Android - NDK - sqlite_第11张图片

你可能感兴趣的:(java,sql,数据库)