按照一下配置,基本能保证demo跑通
1、下载NDK,下载需要的版本
2、下载Cmake版本
3、项目结构:含C++源码
4、编写JNI的加载类
public class YjkModel {
static {
System.loadLibrary("nativ"); //跟CMakeLists.txt 库名一致
}
public native String getJsonData(String path);//通过C++读写数据库,并返回数据,path为数据库的地址,存在assests目录下
public native int writeJsonData(String path,String modifyJson); //path数据库地址
}
5、Java调用方(完整代码已贴出)
String newFilePath = getApplicationContext().getDatabasePath("ctest.ydb").getPath(); // data/data目录
File file = new File(newFilePath);
if (!file.exists()) {
copyFile(SecondActivity.this, "ctest.ydb", newFilePath);
}
YjkModel model = new YjkModel();
String json = model.getJsonData(newFilePath);
public static void copyFile(Context context,String filename, String newFilePath) {
InputStream in = null;
FileOutputStream out = null;
// path为指定目录
File file = new File(newFilePath);
if (!file.exists()) {
try {
in = context.getAssets().open(filename); // 从assets目录下复制
out = new FileOutputStream(file);
int length = -1;
byte[] buf = new byte[1024];
while ((length = in.read(buf)) != -1) {
out.write(buf, 0, length);
}
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
6、配置CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1)
project("nativ")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
add_library(
# 库名,跟Jni加载类中System.loadLibrary("nativ")一致;跟上面的project("nativ")一致
nativ
SHARED
# 项目中用到的C++文件在这里引入
native-lib.cpp
DBoperate.cpp
DBoperate.hpp
cJSoperate.cpp
sqlite3.c
sqlite3.h
cJSON.c
cJSON.h)
find_library(
log-lib
log)
target_link_libraries(
nativ
${log-lib})
CMake版本需要在progect的gradle文件中配置,当前的版本是3.22.1
同样,C++版本配置,也需要在progect的gradle文件中配置,版本是C++11
plugins {
id 'com.android.application'
}
android {
namespace 'com.test.nativ'
compileSdk 32
defaultConfig {
applicationId "com.test.nativ"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags '-std=c++11'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
7、native-lib.cpp(动态注册JNI方法,以及如何调用C++函数)
注:静态注册JNI方法也可行,但是后期维护麻烦,这里推荐动态注册JNI的方法
#include <jni.h>
#include <string>
#include "DBoperate.hpp"
#include "cJSON.h"
static jstring getJsonData(JNIEnv *env, jobject thisObj, jstring path) {
const char *dbPath = env->GetStringUTFChars(path, JNI_FALSE);
DBoperate db;
bool ret = db.openDB(dbPath);
cJSON *global = db.jsonGenerate();
char *json_data = cJSON_Print(global); //JSON数据结构转换为JSON字符串
printf("%s\n", json_data);//输出字符串
db.closeDB();
return env->NewStringUTF(json_data);
}
static jint writeJsonData(JNIEnv *env, jobject thisObj, jstring path,jstring modifyJson) {
const char *dbPath = env->GetStringUTFChars(path, JNI_FALSE);
DBoperate db;
bool ret = db.openDB(dbPath);
const char *modifyPath = env->GetStringUTFChars(modifyJson, JNI_FALSE);
int result= db.ModifyYdb(modifyPath);
db.closeDB();
return result;
}
/**
* 动态注册方案
* 绑定对应的方法映射表
*/
static const JNINativeMethod methods[] = {
{"getJsonData", "(Ljava/lang/String;)Ljava/lang/String;", (jstring *) getJsonData},
{"writeJsonData", "(Ljava/lang/String;Ljava/lang/String;)I", (jstring *) writeJsonData},
};
int jniRegisterNativeMethods(JNIEnv *env,
const char *className,
const JNINativeMethod *gMethods,
int numMethods) {
jclass clazz;
int tmp;
printf("Registering %s natives\n", className);
clazz = env->FindClass(className);
if (clazz == NULL) {
printf("Registering registration "
"unable to find class '%s'\n", className);
return -1;
}
if ((tmp = env->RegisterNatives(clazz, gMethods, numMethods)) < 0) {
printf("Registering failed for '%s', %d\n", className, tmp);
return -1;
}
return 0;
}
int registerNativeMethods(JNIEnv *env) { //com/test/nativ/YjkModel 为JNI类的全路径
return jniRegisterNativeMethods(env, "com/test/nativ/YjkModel", methods,
sizeof(methods) / sizeof(methods[0]));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
jint result = JNI_ERR;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}
if (registerNativeMethods(env) != JNI_OK) {
return -1;
}
result = JNI_VERSION_1_4;
printf("Registering jni load start: %d", result);
return result;
}
关于对native-lib.cpp中重要内容介绍:
1. java层在调用System.loadLibrary()加载动态库后,java虚拟机就会去该库中寻找JNI_OnLoad这个函数,如果有则会调用它,我们的动态注册也就是在这里完成的,所以如果想实现动态注册,就必须实现JNI_OnLoad这个函数;
2.动态注册方案 绑定对应的方法映射表,需要先获取函数的签名,看第三步
static const JNINativeMethod methods[] = {
{"getJsonData", "(Ljava/lang/String;)Ljava/lang/String;", (jstring *) getJsonData},
{"writeJsonData", "(Ljava/lang/String;Ljava/lang/String;)I", (jstring *) writeJsonData},
};
3.如何获取函数签名,使用命令
javac YjkModel.java (生成.class文件)
javap -s -p YjkModel.class (生成函数签名)
4、JNI签名信息介绍:
格式:(参数1类型标识 参数2类型标识 …参数n类型标识)返回值类型标识
例如:JAVA层的native函数
public native String getJsonData(String path);
它对应的JNI层签名信息如下:
(Ljava/lang/String;)Ljava/lang/String;
其中括号内部是参数类型的标识,最右边是返回值类型标识的。