JNI的环境准备就不说了, 网上大把大把的, 这里讲到的内容层次稍微深一点, 涉及Java和C的类型问题, 并配了实际案例的源代码。
JNI大体的流程是: Java端发起调用, 使C端的代码运行, 此时从Java侧传过来的对象, 可以在C端运行.
首先谈谈Java侧准备的代码:
1. 包含Native 方法的class
2. 通过JavaH, 将1中的Native方法生成C的函数原型. 这里完全可以不要, 只要你懂得以下规则:
- 为了管理众多的Native方法(也可以说是区别Native方法), 需要C函数原型中夹带包名, 类名, 函数名
- 由于C无法重载, 因此需要将这些包名冠在C函数名中.
3. 任意情况下Native 方法中的两个必带参数JNIEnv *env, jobject obj, 前者为JNI环境指针, 方便任意时刻,将Java对象转化为C变量, 以便我们处理。 后者为传递的当前Java对象(猜的, 要验证一下)
JNI的的问题一般限于以下类型:
参数传递
参数如何从Java代码传递给C代码的,这里仅仅牵涉到几种基本类型:
jstring, jboolean, jobject,jint,jdouble, 比较简单, JavaH编译出来的头文件就说名问题了.
参数转化
传递过来j-参数,怎么像使用C代码一样使用?这里就有类型转化的问题。
a>基本类型可以直接使用, 如double和jdouble可以互用。
b> java对象使用
对象有String对象和通用的Object对象。 处理的方式有点不同。
不要再C/C++中调用Java的方法, C/C++处理完之后, 将数据对象传出就OK
b1>String对象
GetStringUTFChars取得从Java传入过来的String对象
ReleaseStringUTFChars 用完之后需要释放
b2>Object对象
先得到对象的类句柄:
jclass objectClass = (env)->FindClass("com/ostrichmyself/jni/Structure");
然后取该类中, 需要的域句柄:
jfieldID str = (env)->GetFieldID(objectClass,"nameString","Ljava/lang/String;");
jfieldID ival = (env)->GetFieldID(objectClass,"number","I");
传入的jobject对象, 直接对各个域赋值。
(env)->SetObjectField(theObjet,str,(env)->NewStringUTF("my name is D:"));
(env)->SetShortField(theObjet,ival,10);
如果没有传入对象,而是新生成对象,则可以通过
jobject myNewObjet = env->AllocObject(objectClass);
Java数组的处理
用GetObjectArrayElement取传入的数组
用NewObjectArray 构造一个数组
资源释放原则
a1>C/C++ new的对象或者Malloc的对象,当然要由C/C++去释放
a2>通过JNIEnv的方法new的对象, 如果不需要给Java使用,则必须释放
a3>通过GetStringUTFChars转化从Java得到的String对象到UTF,需要开辟内存,使用完Char*时,
务必释放内存,方法是:ReleaseStringUTFChars
基本涵盖了Java调用C++/C的语法,注意以下问题:
- JNI没有两端, C调用java和java调用c实质都是一样的.也都称为JNI. 没有第二种称呼
- 当C中存在耗时操作, 比如耗时的数据处理之后,gui更新, 可以通过java传递一个引用到C, 从而方便处理结束后调用, 这就是传说中的C调用java.
上述代码都在下面的链接中:
Native侧为C++代码(不是可编译工程, 但在vs2005下编译执行都已经通过)
Native侧为纯C代码(Cgwin 下的MakeFile)
Native侧如何调用Java对象方法代码(Cgwin 下的MakeFile)
thanks!
后记, 发现一偏IBM较新的技术文章, 非常不错, 推荐给大家:
《使用 Java Native Interface 的最佳实践》
http://www.ibm.com/developerworks/cn/java/j-jni/
资料从twitter上获取, 欢迎follow: @ostrichmyself
可以共享资料 ^-^