JNI学习笔记(八)—— 调用接口

这一节的主要内容是讲如何在native程序中嵌入一个java VM。一个java的实现通常是作为一个native库运行。native程序可以和这个库链接,并且用调用接口来加载java VM。事实上,JDK或者java2 sdk的标准启动命令,就是一个简单链接了java VM的c程序。该启动器,解析命令行参数、加载VM、并且通过调用接口运行java程序。



创建java虚拟机

先看一C程序,它加载一个java VM并且调用Prog.main方法:
public class Prog {
    public static void main(String[] args) {
         System.out.println("Hello World " + args[0]);
    }
}
下面是C的代码:
#include <jni.h>
#define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */
#define USER_CLASSPATH "." /* where Prog.class is */
main() {
    JNIEnv *env;
    JavaVM *jvm;
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;
#ifdef JNI_VERSION_1_2
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=" USER_CLASSPATH;
    vm_args.version = 0x00010002;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = JNI_TRUE;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
    JDK1_1InitArgs vm_args;
    char classpath[1024];
    vm_args.version = 0x00010001;
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
    /* Append USER_CLASSPATH to the default system class path */
    sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
    vm_args.classpath = classpath;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */
    if (res < 0) {
        fprintf(stderr, "Can't create Java VM\n");
        exit(1);
    }
    cls = (*env)->FindClass(env, "Prog");
    if (cls == NULL) {
        goto destroy;
}
    mid = (*env)->GetStaticMethodID(env, cls, "main",
                                    "([Ljava/lang/String;)V");
    if (mid == NULL) {
        goto destroy;
    }
    jstr = (*env)->NewStringUTF(env, " from C!");
    if (jstr == NULL) {
        goto destroy;
    }
    stringClass = (*env)->FindClass(env, "java/lang/String");
    args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
    if (args == NULL) {
        goto destroy;
    }
    (*env)->CallStaticVoidMethod(env, cls, mid, args);
destroy:
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
    }
    (*jvm)->DestroyJavaVM(jvm);
}



和java VM一起链接native程序

如何和java VM链接,依赖于native程序对准备知部署于一个特定的java VM还是和各种VM实现一起工作。


和一个已知的java VM链接

例如和上诉代码invoke.c一起链接:
1)在linux:
    cc -I<jni.h dir> -L<libjava.so dir> -lthread -ljava invoke.c
-lthread选项表示需要native线程支持。
-java选项指定了libjava.so是linux中java VM实现的动态库。

2)在win32:
    cl -I<jni.h dir> -MD invoke.c -link <javai.lib dir>\javai.lib
-MD选项保证了native程序时和win32多线程C库一起链接的。


和未知java VM 链接

可以以动态的方式和未知的java VM链接在一起。这样一套程序,就可以在不同的vm实现上执行了。这个方案的核心是,在运行时加载动态链接库。(和前面提到的链接一个已知的java vm,这两种方式,其实就是如何使用动态库的两种方式)。

例如为一个创建java VM的 函数寻找一个入口点:
/* Win32 version */
void *JNU_FindCreateJavaVM(char *vmlibpath)
{
		/* load the dynamical libraries */
    HINSTANCE hVM = LoadLibrary(vmlibpath);
    if (hVM == NULL) {
        return NULL;
    }
    
    /* get the address of function "JNI_CreateJavaVM" */
    return GetProcAddress(hVM, "JNI_CreateJavaVM");
}

/* linux version */
void *JNU_FindCreateJavaVM(char *vmlibpath)
{
		/* load the dynamical libraries */
    void *libVM = dlopen(vmlibpath, RTLD_LAZY);
    if (libVM == NULL) {
        return NULL;
    }
    
    /* get the address of function "JNI_CreateJavaVM" */
    return dlsym(libVM, "JNI_CreateJavaVM");
}


关联native 线程

假设你有一个用native C实现的多线程web server程序。当HTTP请求进来时, 服务端,创建多个native线程来并发地处理请求。我们可能希望在服务中嵌入一个java虚拟机,这样,同一时刻可以有多个线程在这个虚拟机上进行操作。例如:


服务器产生的native方法的生命周期,可能比java VM要短,于是我们要有一种能够将线程附加到已经存在的java VM上的方法,使得VM能够执行附加了的native线程的JNI调用。并且在完成后分离该线程,而不中断其他附加的线程。

下面例子展示了,如何将一个native线程通过调用接口来附加到java VM上:
/* Note: This program only works on Win32 */
#include <windows.h>
#include <jni.h>
JavaVM *jvm; /* The virtual machine instance */
#define PATH_SEPARATOR ';'
#define USER_CLASSPATH "." /* where Prog.class is */
void thread_fun(void *arg)
{
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;
    JNIEnv *env;
    char buf[100];
    int threadNum = (int)arg;

    /* Pass NULL as the third argument */
 #ifdef JNI_VERSION_1_2
    res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);
 #else
    res = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
 #endif

    if (res < 0) {
       fprintf(stderr, "Attach failed\n");
       return;
    }
    cls = (*env)->FindClass(env, "Prog");
    if (cls == NULL) {
        goto detach;
    }
    mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
    if (mid == NULL) {
        goto detach;
    }
    sprintf(buf, " from Thread %d", threadNum);
    jstr = (*env)->NewStringUTF(env, buf);
    if (jstr == NULL) {
        goto detach;
    }
    stringClass = (*env)->FindClass(env, "java/lang/String");
    args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
    if (args == NULL) {
        goto detach;
    }
        (*env)->CallStaticVoidMethod(env, cls, mid, args);
 detach:
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
    }
    (*jvm)->DetachCurrentThread(jvm);
}

main() {
    JNIEnv *env;
    int i;
    jint res;
#ifdef JNI_VERSION_1_2
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=" USER_CLASSPATH;
    vm_args.version = 0x00010002;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = TRUE;
    
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);		
#else
    JDK1_1InitArgs vm_args;
    char classpath[1024];
    vm_args.version = 0x00010001;
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
		
    /* Append USER_CLASSPATH to the default system class path */
    sprintf(classpath, "%s%c%s",vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
    vm_args.classpath = classpath;
    
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
		
#endif /* JNI_VERSION_1_2 */
    if (res < 0) {
        fprintf(stderr, "Can't create Java VM\n");
        exit(1);
    }
    for (i = 0; i < 5; i++)
        /* We pass the thread number to every thread */
        _beginthread(thread_fun, 0, (void *)i);
    
    Sleep(1000); /* wait for threads to start */

    (*jvm)->DestroyJavaVM(jvm);
}

执行结果:
Hello World from thread 1
Hello World from thread 0
Hello World from thread 4
Hello World from thread 2
Hello World from thread 3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值