国庆长假,基本都窝在家里,呵呵,有时间正好把前一段时间做的东西整理出来,省得以后又忘记再去查资料。
做过JAVA开发的朋友都知道,java开发的applet在每次启动时都会弹出一个DOS窗口,这个控制窗口让你开发的非常出色的界面失色不少。那怎么出除这个启动窗口呢?其实很简单,大家可能都用过eclipse,它就是java开发的,而它启动时跟VC、DEPHI做的一样不会弹出那个可恶的DOS窗口, 这是因为它使用了JNI技术,可以通过本地GUI程序直接启动JAVA程序,这样让你的程序更加专业。
JNI(java native interface)它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。同时JNI提供的一整套的API,允许将Java虚拟机直接嵌入到本地的应用程序中。JNI大家用的比较多的就是C/C++按照JNI标准进行封闭成动态库,被JAVA程序调用。这里我就不多说了。
C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1.加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针,第二个参数JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,需注意的是第三个参数,在jdk1.1与1.2版本有些不同,在JDK 1.1中第三个参数总是指向一个结构JDK1_ 1InitArgs,这个结构无法完全在所有版本的虚拟机中进行无缝移植。所以为了保证可移植性,建议使用jdk1.2的方法加载虚拟机。
2.获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;二是通过对象直接得到类定义GetObjectClass
3.获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
参数说明:JNIEnv *env初始化是得到的JNI环境;jclass class前面已获取到的类定义;const char *name方法名;const char *sig方法的定义,我们知道JAVA支持多态,同名方法通过第四个参数来定位得到具体的方法,那么这个参数怎么填呢?我们如何能知道应该用什么样的字符串来表示?在JDK已经准备好了一个反编译工具,通过这个工具我们就可察看类中的属性及方法的定义,如何做?很简单,假如我们在之前建立了一个MyTest.java,通过javac已经编译好此程序,MyTest.java如下:
4.调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5.获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6.数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、NewBooleanArray、NewStringUTF、NewCharArray、NewByteArray、NewString,访问通过GetBooleanArrayElements、GetIntArrayElements等函数。
7.异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8.多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。JNI提供了两个函数:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
那么在命令行运行:javap -s -p MyTest ,你将看到如下输出:
Compiled from "MyTest.java"
public class MyTest extends java.lang.Object{
public java.lang.String helloword;
Signature: Ljava/lang/String;
public MyTest();
Signature: ()V
public MyTest(java.lang.String);
Signature: (Ljava/lang/String;)V
public java.lang.String HelloWord();
Signature: ()Ljava/lang/String;
public void throwExcp() throws java.lang.IllegalAccessException;
Signature: ()V
}
C代码testjava.c:
编译:
在linux下:cc -o testjava testjava.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JAVA_HOME}/jre/lib/i386/client -ljvm
运行结果:
GetMethodID
GetMethodID OK
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
MyTest:this is a test for c call java
NewObject OK
GetMethodID 'HelloWord' OK
JAVA-CLASS:MyTest method:HelloWord:this is a test for c call java
Java VM destory.
上面是一个非常简单的例子,你还可以访问类属性,访问静态方法。这样在C中就能跟JAVA里一样调用它的类、方法、访问它的属性。怎么样,很简单吧?
:)