整体思路:将需要调用的C++函数在java中声明一个对应的native函数,利用javah生成对应的h头文件,用C++封装成动态链接库,提供给java调用
实践步骤:
1.声明需要调用的C++函数在java中对应的native函数,这里只有两个是native函数,只要这2个函数在动态库中能找到就可以了:
package com.ylzhang.service.config;
/**
* YlzhangJNITestNative
* * @author ylzhang
* @desc
* @creatTime 16:29 2017/7/24
*/
public class YlzhangJNITestNative {
public native int testOne(String str);
public native String testTwo(String str);
private String nowFilePath;
public YlzhangJNITestNative (String libPath) throws Exception {
if (null == libPath) {
NativeLibraryLoader.getInstance().loadLibrary();
} else {
System.out.println("[加载] 外部动态库:" + libPath);
System.load(libPath);
}
}
public String getNowFilePath() {
return nowFilePath;
}
public void setNowFilePath(String nowFilePath) {
this.nowFilePath = nowFilePath;
}
}
2.生成native函数对应的头文件,注意这里我是有包名的,javah生成的时候,需要带上路径(不带路径的话,会找不到native函数在C++中的实现),另外我只需要testOne和testTwo有对应头文件函数就可以了,其他函数不需要头文件函数:
2.1 新建/com/ylzhang/service/config/YlzhangJNITestNative.java 文件,内容如下:
package com.ylzhang.service.config;
/**
* YlzhangJNITestNative
*
* @author ylzhang
* @desc
* @creatTime 16:29 2017/7/24
*/
public class YlzhangJNITestNative {
public native int testOne(String str);
public native String testTwo(String str);
}
2.2 生成对应的class文件,运行:
javac YlzhangJNITestNative.java
2.3 然后回到com路径的上一级,运行:
javah com.ylzhang.service.config.YlzhangJNITestNative
此时应该会生成 com_ylzhang_service_config_YlzhangJNITestNative.h 文件,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ylzhang_service_config_YlzhangJNITestNative */
#ifndef _Included_com_ylzhang_service_config_YlzhangJNITestNative
#define _Included_com_ylzhang_service_config_YlzhangJNITestNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ylzhang_service_config_YlzhangJNITestNative
* Method: testOne
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_ylzhang_service_config_YlzhangJNITestNative_testOne
(JNIEnv *, jobject, jstring);
/*
* Class: com_ylzhang_service_config_YlzhangJNITestNative
* Method: testTwo
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ylzhang_service_config_YlzhangJNITestNative_testTwo
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
3.编写对应的C++代码实现上面的头文件函数,并生成动态链接库。例如:C++项目中,新建一个头文件,myJava.h,将com_ylzhang_service_config_YlzhangJNITestNative.h内容复制进去,编写对应的myJava.cpp 实现myJava.h的函数。
myJava.cpp文件如下:
#include "myJava.h"
#include <string>
JNIEXPORT jint JNICALL Java_com_ylzhang_service_config_YlzhangJNITestNative_testOne
(JNIEnv *env, jobject o, jstring s) {
const char* szStr = (env)->GetStringUTFChars(s,0);
int i = MyCppFuncOne(szStr); //调用C++中的其他函数
(env)->ReleaseStringUTFChars(s, szStr );
return (jint)i;
}
JNIEXPORT jstring JNICALL Java_com_ylzhang_service_config_YlzhangJNITestNative_testTwo
(JNIEnv *env, jobject o, jstring s){
const char* szStr = (env)->GetStringUTFChars(s,0);
string sResult;
char* pcResult = NULL;
int iret = MyCppFuncTwo(szStr,pcResult);
if(pcResult != NULL) {
sResult = pcResult;
deleteMem(pcResult);
}
(env)->ReleaseStringUTFChars(s,szStr);
jstring jstr = (env)->NewStringUTF(sResult.c_str());
return jstr;
}
int MyCppFuncTwo(const char* pStr, char*& pResult){//todo 实现}
int MyCppFuncOne(const char* pStr){//todo 实现}
4.生成动态链接库,上文中的libPath是绝对路径,java就可以调用啦。这里放一个福利,我们可以将动态库打包进jar中,在不指定外部动态库路径的时候,默认加载jar中的动态库。
NativeLibraryLoader类
package com.ylzhang.service.config;
import com.ylzhang.service.util.EnvironmentUtils;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
/**
* NativeLibraryLoader
*
* @author ylzhang
* @desc
* @creatTime 14:53 2017/7/21
*/
public class NativeLibraryLoader {
private static final NativeLibraryLoader instance = new NativeLibraryLoader();
private static boolean initialized = false;
public static NativeLibraryLoader getInstance() {
return instance;
}
public synchronized void loadLibrary() throws Exception {
loadLibraryFromJar(null);
}
private void loadLibraryFromJar(final String tmpDir)
throws Exception {
if (!initialized) {
String nativeLibName = EnvironmentUtils.getJniLibraryName();
System.out.println("[加载] 内置动态库");
final File temp;
if (tmpDir == null || tmpDir.equals("")) {
temp = File.createTempFile(EnvironmentUtils.getFilePrefix(), EnvironmentUtils.getJniLibraryExtension());
} else {
temp = new File(tmpDir, nativeLibName);
if (!temp.createNewFile()) {
throw new RuntimeException("File: " + temp.getAbsolutePath()
+ " could not be created.");
}
}
if (!temp.exists()) {
throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist.");
} else {
temp.deleteOnExit();
}
try (final InputStream is = getClass().getClassLoader().
getResourceAsStream(nativeLibName)) {
if (is == null) {
throw new RuntimeException(nativeLibName + " was not found inside JAR.");
} else {
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
System.load(temp.getAbsolutePath());
initialized = true;
}
}
}
EnvironmentUtils类
package com.ylzhang.service.util;
/**
* EnvironmentUtils
* 获取到平台信息 win unix mac
*
* @author ylzhang
* @desc
* @creatTime 14:58 2017/7/21
*/
public class EnvironmentUtils {
private static String OS = System.getProperty("os.name").toLowerCase();
private static String ARCH = System.getProperty("os.arch").toLowerCase();
private static String LIBARAY_NAME_PREFIX = "libontSearch4j";
public static boolean isWindows() {
return (OS.contains("win"));
}
public static boolean isMac() {
return (OS.contains("mac"));
}
public static boolean isUnix() {
return (OS.contains("nix") ||
OS.contains("nux") ||
OS.contains("aix"));
}
private static boolean is64Bit() {
return (ARCH.indexOf("64") > 0);
}
public static String getJniLibraryName() throws Exception {
System.out.println("[平台] os:" + OS + " arch:" + ARCH);
if (isUnix()) {
if (is64Bit()) {
return LIBARAY_NAME_PREFIX + ".so";
}
throw new UnsupportedOperationException("only support x64");
} else if (isWindows()) {
if (is64Bit()) {
return LIBARAY_NAME_PREFIX + ".dll";
}
throw new UnsupportedOperationException("only support x64");
} else if (isMac()) {
throw new UnsupportedOperationException("mac is not support");
}
throw new UnsupportedOperationException();
}
public static String getJniLibraryExtension() {
return (isWindows()) ? ".dll" : ".so";
}
public static String getFilePrefix() {
return LIBARAY_NAME_PREFIX;
}
}