您的位置:首页 > 编程语言 > Java开发

实验: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 :)"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: