java c通过jni相互调用详细例子

1 java

注意事项,如果c中引用的动态库太多,则java无法加载。此时应将C程序改成Socket Udp服务,然后java通过网络与c程序进行交互。

1.1 KcConfig

类名的第一个C表示此类和C共享。

package cn.kuncb;

public class CKcConfig {

    int httpTimeout;
    boolean isSeed;
    String name;

    public CKcConfig(int httpTimeout, boolean isSeed, String name) {
        this.httpTimeout = httpTimeout;
        this.isSeed = isSeed;
        this.name = name;
    }

    public int getHttpTimeout() {
        return httpTimeout;
    }

    public void setHttpTimeout(int httpTimeout) {
        this.httpTimeout = httpTimeout;
    }

    public boolean isSeed() {
        return isSeed;
    }

    public void setSeed(boolean seed) {
        isSeed = seed;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


1.2 KcUseConfig

类名的第一个C表示此类和C共享,第二个C表示调用C方法。

package cn.kuncb;

public class CCKcConfig {
    static {
        //名称要和dll或so文件名称一至
        System.loadLibrary("kc_config");
    }

    //region 初始化

    public native static int registered();

    public native static void unregistered();

    //endregion

    public native static String getName();

    public native static int getHttpTimeout();

    public native static boolean isSeed();

    public native static CKcConfig get();

    public native static void set(CKcConfig config);
}

1.3 java 编译

#先到源码目录D:\MyWork\202109\qinterpolation\src\main\java\cn\kuncb运行
cd D:\MyWork\202109\qinterpolation_java\src\main\java\cn\kuncb
del /Q /F D:\MyWork\202109\qinterpolation_java\src\main\java\cn\kuncb\CCKcConfig.class
javac -encoding UTF-8 CKcConfig.java CCKcConfig.java

1.3 生成头文件

#回退至包目录
cd D:\MyWork\202109\qinterpolation_java\src\main\java
del /Q /F D:\MyWork\202109\qinterpolation_java\src\main\java\cn\kuncb\cn_kuncb_CCKcConfig.h
javah -encoding UTF-8 cn.kuncb.CCKcConfig
  • 生成的头文件完整路径“D:\MyWork\202109\qinterpolation_java\src\main\java\cn_kuncb_CCKcConfig.h”。
  • 复制cn_kuncb_CCKcConfig.h、%JDK_HOME%\include\jni.h和%JDK_HOME%\include\win32\jni_md.h至C项目目录
copy /Y D:\MyWork\202109\qinterpolation_java\src\main\java\cn_kuncb_CCKcConfig.h D:\MyWork\202109\kc_config
copy /Y %JAVA_HOME%\include\jni.h D:\MyWork\202109\kc_config
copy /Y %JAVA_HOME%\include\win32\jni_md.h D:\MyWork\202109\kc_config

1.4 查看java方法对应的签名

javap -classpath D:\MyWork\202109\qinterpolation_java\src\main\java -s cn.kuncb.CKcConfig
#本例输出
public class cn.kuncb.CKcConfig {
  public int test;
    descriptor: I
  public cn.kuncb.CKcConfig(int, boolean, java.lang.String);
    descriptor: (IZLjava/lang/String;)V

  public int getHttpTimeout();
    descriptor: ()I

  public void setHttpTimeout(int);
    descriptor: (I)V

  public boolean isSeed();
    descriptor: ()Z

  public void setSeed(boolean);
    descriptor: (Z)V

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;

  public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V
}

在输出结果中 descriptor:后面的内容即为方法签名。当写C时GetMethodID函数最后一个参数为方法签名。
例如:“getName"为方法名,”()Ljava/lang/String;"为方法签名,此时GetMethodID的参数如下设置:

jmetGetHttpTime = (*env)->GetMethodID(env, exClass, "getName", "()Ljava/lang/String;");

1.5 在idea中添加java.library.path

点击“Edit Configurations->Modify options->Add VM options”中填入以下内容,指明dll或so库所在目录

-Djava.library.path=D:/MyWork/202109/x64/Release

2 C文件

2.1 cn_kuncb_CCKcConfig.h

cn_kuncb_CCKcConfig.h头文件是由javah生成的,复制至项目即可。头文件名及函数定义必须符合java标准,头文件名:包_类.h,函数名:包_类_函数名,其中包名中的.用_代替。
修改头文件第2行

#include <jni.h>

修改为

#include "jni.h"

修改后的源文件“cn_kuncb_KcConfig.h”

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class cn_kuncb_CCKcConfig */

#ifndef _Included_cn_kuncb_CCKcConfig
#define _Included_cn_kuncb_CCKcConfig
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    registered
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_cn_kuncb_CCKcConfig_registered
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    unregistered
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_cn_kuncb_CCKcConfig_unregistered
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    getName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_cn_kuncb_CCKcConfig_getName
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    getHttpTimeout
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_cn_kuncb_CCKcConfig_getHttpTimeout
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    isSeed
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_cn_kuncb_CCKcConfig_isSeed
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    get
 * Signature: ()Lcn/kuncb/CKcConfig;
 */
JNIEXPORT jobject JNICALL Java_cn_kuncb_CCKcConfig_get
  (JNIEnv *, jclass);

/*
 * Class:     cn_kuncb_CCKcConfig
 * Method:    set
 * Signature: (Lcn/kuncb/CKcConfig;)V
 */
JNIEXPORT void JNICALL Java_cn_kuncb_CCKcConfig_set
  (JNIEnv *, jclass, jobject);

#ifdef __cplusplus
}
#endif
#endif

2.2 KcConfig.h

字段保持和java类CCKcConfig一至

#ifndef KC_CEE1D479_955D_624E_8B78_039F9D777D8D
#define KC_CEE1D479_955D_624E_8B78_039F9D777D8D

#include <stdint.h>
#include <stdbool.h>

/*
* 对应java中的cn.kunc.CKcConfig类
*/
struct KcConfig {
	int32_t test;
	int32_t http_timeout;
	bool isseed;
	//可伸缩字符串
	char name[0];
};

#endif	/* KC_CEE1D479_955D_624E_8B78_039F9D777D8D */

2.2 cn_kuncb_CCKcConfig.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cn_kuncb_CCKcConfig.h"
#include "KcConfig.h"


#define KC_SAFE_FREE(ptr) do {\
	if(NULL !=(ptr)  ) { \
		free((ptr)); (ptr) = NULL; \
	} \
} while (0)


static struct KcConfig* global_config = NULL;

//抛出NoClassDefFoundError异常
jint throwNoClassDefError(JNIEnv* env, char* message) {
	jclass exClass;
	const char* className = "java/lang/NoClassDefFoundError";

	exClass = (*env)->FindClass(env, className);
	if (NULL == exClass)
		return throwNoClassDefError(env, message);
	return (*env)->ThrowNew(env, exClass, message);
}
//抛出NullPointerException异常
jint throwNullPointerError(JNIEnv* jenv, char* message) {
	jclass exClass;
	const char* className = "java/lang/NullPointerException";

	exClass = (*jenv)->FindClass(jenv, className);
	if (NULL == exClass)
		return throwNoClassDefError(jenv, message);
	return (*jenv)->ThrowNew(jenv, exClass, message);
}

//抛出NoSuchMethod异常
jint throwNoSuchMethodError(JNIEnv* jenv, char* message) {
	jclass exClass;
	const char* className = "java/lang/NoSuchMethodError";

	exClass = (*jenv)->FindClass(jenv, className);
	if (NULL == exClass)
		return throwNoClassDefError(jenv, message);
	return (*jenv)->ThrowNew(jenv, exClass, message);
}

/// <summary>
/// 初始化
/// </summary>
jint JNICALL Java_cn_kuncb_CCKcConfig_registered(JNIEnv* env, jclass obj) {

	struct KcConfig* config = NULL;

	if (NULL != global_config)
		return 0;


	config = (struct KcConfig*)calloc(1, sizeof(struct KcConfig) + 25); //字符串可以保存24个字符
	if (NULL == config) {
		printf("out of memory.\n");
		goto KC_ERROR_CLEAR;
	}
	memcpy(config->name, "ABCDEFGHIJKLMNOPQRSTUVWZ", 24);
	config->test = 5;
	config->http_timeout = 10;
	config->isseed = false;
	global_config = config;
	return 0;
KC_ERROR_CLEAR:
	KC_SAFE_FREE(config);
	return 1;
}

/// <summary>
/// 释放资源
/// </summary>
void JNICALL Java_cn_kuncb_CCKcConfig_unregistered(JNIEnv* env, jclass obj) {
	if (NULL == global_config)
		throwNullPointerError(env, ("kcconfig not initialized."));
	KC_SAFE_FREE(global_config);
}

jstring JNICALL Java_cn_kuncb_CCKcConfig_getName(JNIEnv* env, jclass obj) {
	if (NULL == global_config)
		throwNullPointerError(env, ("kcconfig not initialized."));
	//C风格函数
	return (*env)->NewStringUTF(env, global_config->name);
	//cpp风格函数
	//return jenv->NewStringUTF(global_config->appname);
}

jint JNICALL Java_cn_kuncb_CCKcConfig_getHttpTimeout(JNIEnv* env, jclass obj) {
	if (NULL == global_config)
		throwNullPointerError(env, ("kcconfig not initialized."));
	return global_config->http_timeout;
}

jboolean JNICALL Java_cn_kuncb_CCKcConfig_isSeed(JNIEnv* env, jclass obj) {
	if (NULL == global_config)
		throwNullPointerError(env, ("kcconfig not initialized."));
	return global_config->isseed;
	//return '\0'; //false 
	//return '\1'; //true
}

jobject JNICALL Java_cn_kuncb_CCKcConfig_get(JNIEnv* env, jclass obj) {
	jclass exClass;
	jmethodID jmethondConstruct;
	jfieldID jFieTest;
	const char* CLASSNAME = "cn/kuncb/CKcConfig";

	if (NULL == global_config)
		throwNullPointerError(env, ("kcconfig not initialized."));

	exClass = (*env)->FindClass(env, CLASSNAME);
	if (NULL == exClass)
		throwNoClassDefError(env, "java class cn.kuncb.CKcConfig not foud.");
	jmethondConstruct = (*env)->GetMethodID(env, exClass, "<init>", "(IZLjava/lang/String;)V");

	if (NULL == jmethondConstruct)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig Construct method not foud.");

	jFieTest = (*env)->GetFieldID(env, exClass, "test", "I");
	if (NULL == jFieTest)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig field test not foud.");


	jobject jobj = (*env)->NewObject(env, exClass, jmethondConstruct,
		global_config->http_timeout,
		global_config->isseed,
		(*env)->NewStringUTF(env, global_config->name)
	);
	(*env)->CallVoidMethod(env, exClass, jmethondConstruct);
	global_config->test = 1;
	(*env)->SetIntField(env, exClass, jFieTest, global_config->test);
	return jobj;
}

void Java_cn_kuncb_CCKcConfig_set(JNIEnv* env, jclass obj, jobject objArg) {
	jclass exClass;
	jfieldID jFieTest;
	jmethodID jmetGetHttpTime, jmetIsSeed, jmetGetName;
	jint test = 0, http_timeout = 0;
	jboolean isseed = true;
	jobject name = NULL;

	const char* strName = NULL;
	jboolean isCopy;
	jsize strNameLen = 0;
	struct KcConfig* newConfig;

	if (NULL == global_config)
		throwNullPointerError(env, "kcconfig not initialized11.");

	exClass = (*env)->GetObjectClass(env, objArg);
	if (NULL == exClass)
		throwNoClassDefError(env, "java class cn.kuncb.CKcConfig not foud.");

	jFieTest = (*env)->GetFieldID(env, exClass, "test", "I");
	if (NULL == jFieTest)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig field test not foud.");
	test = (*env)->GetIntField(env, exClass, jFieTest);

	jmetGetHttpTime = (*env)->GetMethodID(env, exClass, "getHttpTimeout", "()I");
	if (NULL == jmetGetHttpTime)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig method getHttpTimeout not foud.");

	jmetIsSeed = (*env)->GetMethodID(env, exClass, "isSeed", "()Z");
	if (NULL == jmetIsSeed)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig method isSeed not foud.");

	jmetGetName = (*env)->GetMethodID(env, exClass, "getName", "()Ljava/lang/String;");
	if (NULL == jmetGetName)
		throwNoSuchMethodError(env, "java class cn.kuncb.CKcConfig method getName not foud.");

	//在这里会抛出异常,name or signature does not match,不知道是啥原因,而且接收的test值也和java传递过来时的不一至
	http_timeout = (*env)->CallIntMethod(env, exClass, jmetGetHttpTime);
	isseed = (*env)->CallBooleanMethod(env, exClass, jmetIsSeed);
	name = (*env)->CallObjectMethod(env, exClass, jmetGetName);


	if (NULL != name) {
		strName = (*env)->GetStringUTFChars(env, name, &isCopy);
		if (NULL != strName)
			strNameLen = (*env)->GetStringUTFLength(env, name);		//strNameLen=0表示空字符串==""
	}

	newConfig = (struct KcConfig*)realloc(global_config, sizeof(struct KcConfig) + strNameLen + sizeof(char));
	if (NULL == newConfig) { //修改除name之外的字段
		global_config->test = test;
		global_config->http_timeout = http_timeout;
		global_config->isseed = isseed;
	} else {
		if (strNameLen < 1) //空字符串不复制
			memcpy(newConfig->name, strName, strNameLen);
		newConfig->name[strNameLen] = '\0';
		newConfig->http_timeout = http_timeout;
		newConfig->isseed = isseed;
		newConfig->test = test;
		global_config = newConfig;
	}
	(*env)->ReleaseStringUTFChars(env, name, strName);
}

2.3 查看导出的dll

#查看依赖项
dumpbin /DEPENDENTS D:\MyWork\202109\x64\Debug\kc_config.dll
#查看导出的函数
dumpbin /EXPORTS D:\MyWork\202109\x64\Debug\kc_config.dll

3总结

在写C时如果依赖的模块比较少,则可以使用jin,但是如果依赖的模块比较多,jin大概率会加载失败(可能因为jdk版本或系统原因,不确定),因此建议采用以下两种方式实现:

  • 直接将C程序制作为exe然后通过管道调用,此种方法实现简单,但是性能低下,还不如直接java实现;
  • 用C写后台服务,此服务专供java调用,java与C之间通过网络通信来传递消息。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kmblack1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值