接上一篇 <<Eclipse + vs2013 编写第一个 JNI HelloWorld>> ,读者一定很奇怪我们在HelloWorld.java中定义的private native void print()调用时,
怎么就会调用到我们底层的Java_com_worthcloud_HelloWorld_print(JNIEnv *env,jobject obj)函数呢?这里先大概了解一下Java中我们本地库的加载
方式:System.loadlibrary(libraryname)。该函数会在适当的路劲加载动态库文件,然后通过类似于dlsym(handle,"JNI_OnLoad")方法来执行JNI_OnLoad
函数。这样我们就可以在JNI_OnLoad()这样一个函数中做一些事情以影响后续的程序代码执行。我们这章介绍的通过函数名对应表的方式来加载native方法
就是在这里面做了些文章的。
在Java运行时,其内部会维持一个native函数库表,这里我们暂且称为native_sym_tbl,该开始这个表示空的。当在执行到某一个native方法时,例如执行到
new HelloWorld().print()这个natvie方法时,就从native_sym_tbl这个表中查找该print函数对应的底层实现函数原型,而此时该表为空,肯定查找不到,这时就会
采用默认的Java_完整类名_函数名()的方式在动态加载库中查找该函数,针对前面的print()这个函数,就会执行如下查找Java_com_worthcloud_HelloWorld_print()
方法,然后执行该函数,没有找到则直接报错。那么,如果我们通过适当的方法,预先填好native_sym_tbl这个表,则在后续的执行中则可以直接对应到底层的
实现函数,并且这样还未我们带来了一个副产品:可以将Java中声明的native方法名称映射为我们自己的名称,而不需要按照其默认的方式。
看如下实现:
HelloWorld.java
package com.worthcloud;
class HelloWorldUtils
{
public native void print();
}
class Prompt
{
public native String getLine(String prompt);
}
public class HelloWorld {
private native void print();
public native String getLine(String prompt);
public static void main(String[] args) {
// TODO Auto-generated method stub
new HelloWorldUtils().print();
new HelloWorld().print();
while(true)
System.out.println("Output:"+new Prompt().getLine("Input:"));
}
static
{
System.loadLibrary("helloworld");
}
}
#include <jni.h> #include <stdlib.h> #include "jni_helloworld.h" #define HELLOWORLDUTILS_CP "com/worthcloud/HelloWorldUtils" #define HELLOWORLD_CP "com/worthcloud/HelloWorld" #define PROMPT_CP "com/worthcloud/Prompt" /** * Class: com_worthclould_HelloWorldUtils_print * Method: print * Signature: ()V */ JNIEXPORT void JNICALL JNI_HelloWorldUtils_print(JNIEnv *env, jobject obj) { printf("Hello World - (utils)\n"); fflush(stdout); } /** * Class: com_worthclould_HelloWorld_print * Method: print * Signature: ()V */ JNIEXPORT void JNICALL JNI_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World\n"); fflush(stdout); } /** * Class: com_worthcloud_Prompt * Method: getLine * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL JNI_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) { char buf[128]; const char *str; str = (*env)->GetStringUTFChars(env, prompt, NULL); if (str == NULL) return NULL; /*OutOfMemoryError already thrown*/ printf("%s", str); fflush(stdout); (*env)->ReleaseStringUTFChars(env, prompt, str); /** * We assume here that the user does not type more than 127 characters */ scanf_s("%s", buf); return (*env)->NewStringUTF(env, buf); } //接口输出函数表 static JNINativeMethod gHelloWorldUtilsMethods[] = { { "print", "()V", (void *)JNI_HelloWorldUtils_print } }; static JNINativeMethod gHelloWorldMethods[] = { { "print", "()V", (void *)JNI_HelloWorld_print } }; static JNINativeMethod gPromptMethods[] = { { "getLine", "(Ljava/lang/String;)Ljava/lang/String;", (void *)JNI_Prompt_getLine } }; /** * description: 针对类名,注册多个native 接口函数 * * env: 代表Java环境,通过这个环境可以调用JNIEnv中的相关方法 * className: Java类名称 * pMethods: 接口函数表 * nMethods: 接口函数中函数的个数 */ static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *pMethods, int nMethods) { jclass clazz; if ((clazz = (*env)->FindClass(env, className)) == NULL) return -1; if ((*env)->RegisterNatives(env, clazz, pMethods, nMethods) != JNI_OK) return -2; return 0x0; } /** * 方法名称规定: jni 入口函数 * * 参数介绍: * vm: 虚拟机指针 */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; jint result = 0; if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK || env == NULL) { result = -1; goto end; } if (registerNativeMethods(env, HELLOWORLDUTILS_CP, gHelloWorldUtilsMethods, sizeof(gHelloWorldUtilsMethods) / sizeof(JNINativeMethod)) < 0) { result = -2; goto end; } if (registerNativeMethods(env, HELLOWORLD_CP, gHelloWorldMethods, sizeof(gHelloWorldMethods) / sizeof(JNINativeMethod)) < 0) { result = -3; goto end; } if (registerNativeMethods(env, PROMPT_CP, gPromptMethods, sizeof(gPromptMethods) / sizeof(JNINativeMethod)) < 0) { result = -4; goto end; } result = JNI_VERSION_1_4; end: if (result < 0) { printf("JNI_OnLoad failure(ret:%d)\n", result); } return result; }