JNI技术小结
主要内容:
1、Java 通过JNI调用DLL,返回ArrayList.
2、Jsp 通过JNI调用DLL.
使用的开发工具:MyEclipse10.7 + java jdk1.6.0_35 + vs2010.
一、Java层原型方法
package com.zdd.searcher;import java.util.ArrayList;public class SearchEngine {public native ArrayList<QueryResult> query(String imgFileName);static{System.loadLibrary("searchenginedll");}}package com.zdd.searcher;/** * 查询结果项 * @author zdd * */public class QueryResult {private String filePath; //private float similarity; ////构造函数public QueryResult() {filePath="";similarity = 0.0f;}public QueryResult(String filePath, float similarity) {this.filePath = filePath;this.similarity = similarity;}public String toString() {return "filePath: " + filePath + " similarity: " + similarity;}public String getFilePath() {return filePath;}public void setFilePath(String filePath) {this.filePath = filePath;}public float getSimilarity() {return similarity;}public void setSimilarity(float similarity) {this.similarity = similarity;}}二、生成.h文件
\src>javac com/zdd/searcher/SearchEngine.java\src>javah com.zdd.searcher.SearchEngine
三、使用.h写dll项目
使用vs新建win32 dll项目,将上述生成的com_zdd_searcher_SearchEngine.h文件导入项目。
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_zdd_searcher_SearchEngine */#ifndef _Included_com_zdd_searcher_SearchEngine#define _Included_com_zdd_searcher_SearchEngine#ifdef __cplusplusextern "C" {#endif/* * Class: com_zdd_searcher_SearchEngine * Method: query * Signature: (Ljava/lang/String;)Ljava/util/ArrayList; */JNIEXPORT jobject JNICALL Java_com_zdd_searcher_SearchEngine_query (JNIEnv *env, jobject _obj, jstring _imgFileName);#ifdef __cplusplus}#endif#endif若提示没有发现<jni.h>,则在VC++ Directories ->Include中添加JNI头文件:..\Java\jdk1.6.0_35\include和..\Java\jdk1.6.0_35\include\win32。
四、将c++中的vector向量通过Native层以ArrayList集合对象的方式返回给Java.
SearchEngine *searcher = NULL;/* * Class: com_zdd_searcher_SearchEngine * Method: query * Signature: (Ljava/lang/String;)Ljava/util/ArrayList; */JNIEXPORT jobject JNICALL Java_com_zdd_searcher_SearchEngine_query (JNIEnv *env, jobject _obj, jstring _imgFileName){if(searcher == NULL)searcher = new FaceImageSearch();//获取查询结果vector<QueryResult> results;searcher->query(jstringTostring(env, _imgFileName), results);jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//获得ArrayList类引用if(list_cls == NULL) { std::cout << "list_cls is null \n" ; } jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //获得得构造函数Id jobject list_obj = env->NewObject(list_cls , list_costruct); //创建一个Arraylist集合对象 //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ; jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z"); jclass qr_cls = env->FindClass("Lcom/zdd/searcher/QueryResult;");//获得QueryResult类引用 //获得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V //第三个参数根据构造函数的类型设定//例如:"(Ljava/lang/String;F)V"表示Java中方法为void xxx(String, float)其中,xxx表示函数名//-------------------------------------------------//基本JNI域描述符//Z--boolean//B--byte//C--char//S--short//I--int//J--long//F--Float//D--double//引用类型的描述符//Object--Ljava/lang/Object; //注意此处的分号(;)是JNI的一部分,不能省。//String--Ljava/lang/String;//int[]--[I//float[]--[F//String[] -- [Ljava/lang/String;//方法表示示例// int f (int i, Object object) -- (ILjava/lang/Object;)I//void set (byte[ ] bytes) -- ([B)V jmethodID qr_costruct = env->GetMethodID(qr_cls , "<init>", "(Ljava/lang/String;F)V"); for(unsigned int i = 0; i < results.size(); i++){//通过调用该对象的构造函数来new 一个 QueryResult实例 jobject qr_obj = env->NewObject(qr_cls , qr_costruct, stringToJstring(env, results[i].imagePath.c_str()), results[i].similarity); //构造一个对象env->CallBooleanMethod(list_obj , list_add , qr_obj); //执行Arraylist类实例的add方法,添加一个QueryResult对象 }return list_obj;}五、char*和jstring相互传化
//jstring to char*char* jstringTostring(JNIEnv* env, jstring jstr){char* rtn = NULL;jclass clsstring = env->FindClass("java/lang/String");jstring strencode = env->NewStringUTF("utf-8");jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);jsize alen = env->GetArrayLength(barr);jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);if (alen > 0){rtn = (char*)malloc(alen + 1);memcpy(rtn, ba, alen);rtn[alen] = 0;}env->ReleaseByteArrayElements(barr, ba, 0);return rtn;}//char* to jstringjstring stringToJstring(JNIEnv* env, const char* pat){ jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);}其它代码略。
六、编译dll,并将其放项目目录下(和src平行) 在java中使用如下代码测试
package com.zdd.searcher;import java.util.ArrayList;public class DllTest {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubSearchEngine se = new SearchEngine();ArrayList<QueryResult> lists = se.query("");for(int i = 0; i < lists.size(); i++){System.out.println(lists.get(i).getFilePath());}System.out.println("Done.");}}七、在JSP中使用JNI调用dll
JSP本身分为bean的src目录和WebRoot的脚本目录,而我们的dll需要在src下的类中调用,于是遇到了将DLL和资源文件放置到什么地方的问题。并且使用MyEclipse自带的tomcat,不存在../tomcat/bin目录。
最后解决方法:将DLL放置在..\Java\jdk1.6.0_35\bin下,可以访问。