实验:java call so, 传入传出多参数
2017-12-14 04:24
253 查看
前言
java call so, 传入传出多参数的例子,网上有很多同学做过了。但是都没有给出一个可以跑的工程,让我这样的新手很为难。
以前在windows下,做过简单的传出一个字符串的最简单的dll, 供java调用,但是需求就是那么简单,也没作超出需求的实验。
前2天,有个搞java的同事要调用第三方的SDK(C接口),他不玩C,让我帮忙封装一个so, 供他调用。
这就涉及到传入传出多参数的问题了。
本人是java入门级的选手,看了网上诸位同学的总结(都是代码片段),跟着做实验,惭愧,搞了2天,终于做完实验了 :)
收获了一个在linux下用java调用so的工程,挺好用的,基本能满足我同事的需求。如果他需要传其他类型的参数,稍微改改就能用。
工程下载点
java_call_so_return_multi_out_param.7z实验环境
debian7.5, JDK9, g++, source insight安装JDK9
公司有环境,家里没有,先在debian7.5上装一个。网上资料大把,没遇到问题。
工程预览
工程文件共9个\. | build_java_prj.sh | gen_jni_header.sh | go.sh | ReadMe.txt | run_java_prj.sh | +---com | class_fun_ret_result.java | test_so.java | \---test_so main.cpp Makefile
最后写的go.sh, 一键编译java工程,so工程,运行java工程。
# @file go.sh echo off clear chmod 777 ./*.sh ./build_java_prj.sh ./gen_jni_header.sh cd ./test_so make rebuild cd .. ./run_java_prj.sh
运行效果
root@debian750devmin:/home/dev/java_call_so_return_multi_out_param# ./go.sh off build java prj begin, please wait a moment... 总用量 16K -rw-r--r-- 1 root root 280 12月 14 04:05 class_fun_ret_result.class -rw-r--r-- 1 root root 324 12月 14 00:10 class_fun_ret_result.java -rw-r--r-- 1 root root 1.6K 12月 14 04:05 test_so.class -rw-r--r-- 1 root root 1.4K 12月 14 03:41 test_so.java build java prj end :) gen jni header begin, please wait a moment... 警告: 已计划在下一个 JDK 主发行版中删除 javah 工具。该工具在 JDK 8 中已由添加到 javac 的 '-h' 选项取代。建议用户改为使用 javac '-h' 选项; 有关详细信息, 请查看 javac 帮助页。 警告: 已计划在下一个 JDK 主发行版中删除 javah 工具。该工具在 JDK 8 中已由添加到 javac 的 '-h' 选项取代。建议用户改为使用 javac '-h' 选项; 有关详细信息, 请查看 javac 帮助页。 ./com_class_fun_ret_result.h ./com_test_so.h gen jni header end :) find: “./empty_dir”: 没有那个文件或目录 find: “./socket_easy”: 没有那个文件或目录 make clean find: “./empty_dir”: 没有那个文件或目录 find: “./socket_easy”: 没有那个文件或目录 find: “./empty_dir”: 没有那个文件或目录 find: “./socket_easy”: 没有那个文件或目录 make[1]: Entering directory `/home/dev/java_call_so_return_multi_out_param/test_so' -------------------------------------------------------------------------------- make clean rm -f test_so.so ./main.o rm -rf /usr/lib/test_so.so rm -rf ./test_so.so make[1]: Leaving directory `/home/dev/java_call_so_return_multi_out_param/test_so' -------------------------------------------------------------------------------- make all find: “./empty_dir”: 没有那个文件或目录 find: “./socket_easy”: 没有那个文件或目录 make[1]: Entering directory `/home/dev/java_call_so_return_multi_out_param/test_so' g++ -std=c++98 -c -Wall -g -shared main.cpp -o main.o -I. -I./inc -I/home/dev/jdk-9.0.1/include/ -I/home/dev/jdk-9.0.1/include/linux -L/usr/local/lib/ -L./lib/ -lstdc++ -pthread -lpthread -lm -fPIC g++ -std=c++98 -Wall -g -shared -o test_so.so main.o -I. -I./inc -I/home/dev/jdk-9.0.1/include/ -I/home/dev/jdk-9.0.1/include/linux -L/usr/local/lib/ -L./lib/ -lstdc++ -pthread -lpthread -lm -fPIC -------------------------------------------------------------------------------- make all chmod 777 test_so.so find . -name test_so.so ./test_so.so make[1]: Leaving directory `/home/dev/java_call_so_return_multi_out_param/test_so' chmod 777 ./test_so.so cp ./test_so.so /usr/lib ldconfig run java prj begin, please wait a moment... * hello test_so.java str_1 = string from interface Java_test_1so_readData >> Java_test_1so_readData_1string p_str_in = do_some_function this point on java code str_in = do_some_function info_rc.str_name = coder, c++ coder info_rc.str_err_msg = very happy, no mistake happen :) str_rc = << Java_com_test_1so_readData_11string run java prj end :)
工程文件
java工程
// @file class_fun_ret_result.java // @brief as out param to the so interface package com; public class class_fun_ret_result { String str_name; String str_err_msg; }
// @file test_so.java // @brief test so interface(in out multi param) // on linux, javac can't support chinese text // so, if want add comment, please use english text // view this class signature // javap -s test_so package com; import com.class_fun_ret_result; public class test_so { // native method on our .so // method must was static static public native String readData(); static public native String readData_1string(String str_in, class_fun_ret_result info_out); static { // on linux, load .so // on win, load .dll System.load("/usr/lib/test_so.so"); // must .so load by full path name } public static void main(String[] args) { System.out.println("* hello test_so.java"); test_so td = new test_so(); String str_1 = td.readData(); System.out.println("str_1 = " + str_1); String str_in = new String("do_some_function"); class_fun_ret_result info_rc = new class_fun_ret_result(); String str_rc = td.readData_1string(str_in, info_rc); System.out.println("this point on java code"); System.out.println("str_in = " + str_in); System.out.println("info_rc.str_name = " + info_rc.str_name); System.out.println("info_rc.str_err_msg = " + info_rc.str_err_msg); System.out.println("str_rc = " + str_rc); } }
SO工程
// @file main.cpp // @brief jni interface imp // @note // JNI 传参操作 // http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html // 传入string对象,并修改 // http://blog.csdn.net/banketree/article/details/40535325 // ndk 函数 传入的jstring 不能改 // https://stackoverflow.com/questions/7356787/how-to-modify-the-value-of-a-jstring-passed-to-a-c-routine-using-java-and-jni // INI 返回多参数的例子 // http://leave001.blog.163.com/blog/static/162691293201242452251332/ // java类型和c++类型的对应表,c++读写java数据的方法 // http://blog.sina.com.cn/s/blog_414e587f0101411f.html // How to Load a Java Native/Shared Library (.so) // https://www.chilkatsoft.com/java-loadLibrary-Linux.asp // Java - Basic Datatypes // http://www.tutorialspoint.com/java/java_basic_datatypes.htm // jstring 的传入,读取和释放 // https://stackoverflow.com/questions/13796786/jstring-return-in-jni-program #include <stdlib.h> #include <stdio.h> #include <string> #include <unistd.h> #include <getopt.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/select.h> #include "com_test_so.h" jstring pChar2JString(JNIEnv* env, const char* pat); char* JString2pChar(JNIEnv* env, jstring jstr); int main(int argc, char** argv) { return EXIT_SUCCESS; } /* * Class: com_test_so * Method: readData_string * Signature: (Ljava/lang/String;Lcom/class_fun_ret_result;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_test_1so_readData_11string (JNIEnv * p_env, jclass p_class, jstring p_str_in, jobject p_object) { char sz_buf[4096] = {'\0'}; jclass p_jclass_object = NULL; jfieldID p_jfieldID_str_name = NULL; jfieldID p_jfieldID_str_err_msg = NULL; char* p_new_sz = NULL; std::string str_in = ""; printf(">> Java_test_1so_readData_1string\n"); p_new_sz = JString2pChar(p_env, p_str_in); if (NULL != p_new_sz) { str_in = p_new_sz; delete [] p_new_sz; p_new_sz = NULL; printf("p_str_in = %s\n", str_in.c_str()); } // get com.class_fun_ret_result p_jclass_object = p_env->FindClass("com/class_fun_ret_result"); if (NULL == p_jclass_object) { return pChar2JString(p_env, "error : p_env->GetObjectClass(p_env, p_object)"); } // set class_fun_ret_result.str_name p_jfieldID_str_name = p_env->GetFieldID(p_jclass_object,"str_name","Ljava/lang/String;"); if (NULL == p_jfieldID_str_name) { return pChar2JString(p_env, "error : env->GetFieldID(p_jclass_object,\"str_name\",\"Ljava/lang/String;\")"); } p_env->SetObjectField(p_object,p_jfieldID_str_name,p_env->NewStringUTF("coder, c++ coder")); // set class_fun_ret_result.str_err_msg p_jfieldID_str_err_msg = p_env->GetFieldID(p_jclass_object, "str_err_msg", "Ljava/lang/String;"); if (NULL == p_jfieldID_str_err_msg) { return pChar2JString(p_env, "error : p_env->GetFieldID(p_jclass_object, \"str_err_msg\", \"Ljava/lang/String;\")"); } p_env->SetObjectField(p_object, p_jfieldID_str_err_msg, pChar2JString(p_env, "very happy, no mistake happen :)")); // return string memset(sz_buf, 0, sizeof(sz_buf)); sprintf(sz_buf, "%s", "<< Java_com_test_1so_readData_11string"); return pChar2JString(p_env, sz_buf); } /* * Class: com_test_so * Method: readData * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_test_1so_readData (JNIEnv* p_env, jclass p_class) { char sz_buf[4096] = {'\0'}; memset(sz_buf, 0, sizeof(sz_buf)); sprintf(sz_buf, "%s", "string from interface Java_test_1so_readData"); return pChar2JString(p_env, sz_buf); } jstring pChar2JString(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); } char* JString2pChar(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; }
# ============================================================================== # makefile # lostspeed 2017-12-12 # note # when first build on vmware's linux, please adjust date time for build project # e.g. date -s "2017-12-5 21:49:00" # ============================================================================== BIN = test_so.so LINE80 = -------------------------------------------------------------------------------- CC = g++ -std=c++98 CFLAGS = -Wall -g -shared INC = -I. -I./inc -I/home/dev/jdk-9.0.1/include/ \ -I/home/dev/jdk-9.0.1/include/linux # LIBS = -lstdc++ -pthread -lpthread -lm LIBS = -lstdc++ -pthread -lpthread -lm -fPIC LIBPATH = -L/usr/local/lib/ -L./lib/ DEPEND_CODE_DIR = ./empty_dir \ DEPEND_CODE_SRC = $(shell find $(DEPEND_CODE_DIR) -name '*.cpp') DEPEND_CODE_OBJ = $(DEPEND_CODE_SRC:.cpp=.o) # root code dir is ./'s code, e.g. main.cpp ROOT_CODE_SRC = $(shell find ./ -name '*.cpp') ROOT_CODE_OBJ = $(ROOT_CODE_SRC:.cpp=.o) # if no sub code dir, must fill a sub dir exist but empty. e.g. ./empty_dir # if have sub code dir, fill it like DEPEND_CODE_DIR # e.g. ./xx_subdir/xx_type/ SUB_CODE_DIR = ./socket_easy SUB_CODE_SRC = $(shell find $(SUB_CODE_DIR) -name '*.cpp') SUB_CODE_OBJ = $(SUB_CODE_SRC:.cpp=.o) PRJ_PATH = /home/dev/src/test_so help: clear @echo $(LINE80) @echo "ROOT_CODE_SRC = " $(ROOT_CODE_SRC) @echo "ROOT_CODE_OBJ = " $(ROOT_CODE_OBJ) @echo "DEPEND_CODE_DIR = " $(DEPEND_CODE_DIR) @echo "DEPEND_CODE_SRC = " $(DEPEND_CODE_SRC) @echo "DEPEND_CODE_OBJ = " $(DEPEND_CODE_OBJ) @echo "SUB_CODE_DIR = " $(SUB_CODE_DIR) @echo "SUB_CODE_SRC = " $(SUB_CODE_SRC) @echo "SUB_CODE_OBJ = " $(SUB_CODE_OBJ) @echo "INC = " $(INC) clean: @echo $(LINE80) @echo make clean rm -f $(BIN) $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ) rm -rf /usr/lib/test_so.so rm -rf ./test_so.so all:$(BIN) @echo $(LINE80) @echo make all chmod 777 $(BIN) find . -name $(BIN) $(BIN) : $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ) $(CC) $(CFLAGS) -o $@ $^ $(INC) $(LIBPATH) $(LIBS) .cpp.o: $(CC) -c $(CFLAGS) $^ -o $@ $(INC) $(LIBPATH) $(LIBS) rebuild: make clean @echo $(LINE80) make all chmod 777 ./test_so.so cp ./test_so.so /usr/lib ldconfig rebuild_and_run: make rebuild @echo $(LINE80) ./$(BIN)
为了方便,建立的其他sh文件
# @file build_java_prj.sh echo "build java prj begin, please wait a moment..." rm -rf ./com/*.class # 由 .java 生成 .class javac ./com/test_so.java ls -l -h ./com echo "build java prj end :)"
# @file gen_jni_header.sh echo "gen jni header begin, please wait a moment..." rm -rf ./*.h rm -rf ./com/*.h # 由.class 生成 .h # jdk8 javah -jni -classpath /home/dev/java_call_so_return_multi_out_param/ com.class_fun_ret_result javah -jni -classpath /home/dev/java_call_so_return_multi_out_param/ com.test_so ls ./*.h mv ./com_class_fun_ret_result.h ./test_so/ mv ./com_test_so.h ./test_so/ echo "gen jni header end :)"
# @file run_java_prj.sh echo "run java prj begin, please wait a moment..." # run our java prog java com.test_so echo "run java prj end :)"
相关文章推荐
- java 实际应用中的实验与总结
- java实验三 类的继承与多态
- java语言传入一个日期返回星期几
- Java 引用传递的实验
- JAVA--第十一周实验--模拟一个信号灯的软件--代码重构--升级版
- 实验六Java图形用户界面之文本框FocusEvent事件
- Java实验:Java实现从键盘输入4位会员卡号,与随机数比较,百位相等的即为中奖者
- C++调用Python函数并传入传出参数
- C/Java/Python/Objective-C在OS X上的性能实验
- Java实验——排列
- java方法重载实验:判断键盘输入的两个数据的类型后进行比较
- 第五次实验报告 java 网络编程
- DataTables服务器端传入传出(接收与提交)的数据格式 搜索、排序和分页与后台数据的交互
- java 调用存储过程 传入数组
- java后端系统架构之消息队列篇:kafka的实验
- 20145215刘俊谦实验五 Java网络编程及安全
- Java实验报告三
- java实验2
- JAVA--第八周实验--继承的练习
- Java文件操作自己做的一些小实验