静态注册较为简单,只需要在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
- #include
- #include
- #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函数名称。