静态注册
静态注册较为简单,只需要在Java程序中声明jni函数,随后在cpp程序中实现此jni函数即可。
public native String stringFromJNI();//声明
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_hello_1cmake_MainActivity_stringFromJNI(JNIEnv* env,jobject ){
std::string hello = "JNI test demo";
return env->NewStringUTF(hello.c_str());
}//实现函数
静态注册的实现方式为:当java程序第一次使用某个jni函数时,会进行搜索,根据包名-类名-函数名进行搜索,随后与对应的jni函数建立连接,随后便可以调用。
由于虚拟机自动实现了注册匹配,所以在实际编写中比较省力,名字匹配了就可以直接用。
但是静态注册有一些缺点:1.函数名称需要匹配,不能自定义。2.每个jni函数第一次被调用时都需要搜索,增加了额外开销。如果工程中的jni函数较多,在搜索方面可能会花费较多时间。
动态注册
动态注册通过手动注册实现虚拟机与jni的连接。优点是节约了搜索所需的开销,缺点是需要额外进行函数编写,增加了人工工作量。
创建一个java类,里面声明两个jni函数:
package com.example.hello_cmake;
public class TestJni {
static {
System.loadLibrary("native-lib");
}
public native int text(String message);
public static native int static_text(String message);
}
创建一个cpp文件,代码实现如下:
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOG_TAG "CPPLOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
//jni函数实现1
jint native_text(JNIEnv *env, jobject jobject1, jstring msg){
const char *jmsg = env->GetStringUTFChars(msg, JNI_FALSE);
LOGD("message = %s",jmsg);
return 0;
}
//jni函数实现2
jint native_static_text(JNIEnv *env, jobject jobject1, jstring msg){
const char *jmsg = env->GetStringUTFChars(msg, JNI_FALSE);
LOGD("message = %s",jmsg);
return 0;
}
//方法注册结构体
static const JNINativeMethod nativeMethod[] = {
{"text", "(Ljava/lang/String;)I", (void *) native_text},
{"static_text", "(Ljava/lang/String;)I", (void *) native_static_text}
};
//注册函数
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com/example/hello_cmake/TestJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;//返回0代表成功
}
return result;
}
//加载动态库
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
//返回值代表动态库需要的jni版本
return result;
}
}
在活动中进行调用:
TestJni.static_text("it is a static method");
new TestJni().text("it is a normal method");
代码讲解
从cpp代码中可知,为了实现动态注册,额外实现了两个函数与一个结构体。
//加载动态库
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
//返回值代表动态库需要的jni版本
return result;
}
}
首先被调用的是JNI_OnLoad函数,该函数的返回值是需要的jni版本。
需要注意的是,该函数在jni.h中只是进行了声明,并没有被实现,因此在此处可以直接实现。
/*
* Prototypes for functions exported by loadable shared libs. These are
* called by JNI, not provided by JNI.
*/
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
随后被调用的是:
//注册函数
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com/example/hello_cmake/TestJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;//返回0代表成功
}
return result;
}
该函数在JNI_OnLoad函数中被调用,该函数的目的是加载方法注册结构体,将jni函数与java里的声明进行注册链接。
该函数返回值为0时说明方法注册结构体已经加载成功。
方法注册结构体:
//方法注册结构体
static const JNINativeMethod nativeMethod[] = {
{"text", "(Ljava/lang/String;)I", (void *) native_text},
{"static_text", "(Ljava/lang/String;)I", (void *) native_static_text}
};
方法注册结构体是我们自定义函数名称与建立对应关心的核心。
结构体内的成员依次是:java中声明的jni函数名称,java中声明的jni函数的函数签名,自定义实现的jni函数名称。