如何在Java和C++之间进行互相调用呢?
应用场景没有什么好说到了,Java调用底层SDK,SDK代码基本都是C或者C++编写,以及后期的物联网技术,很多都是和底层硬件相关系的,底层硬件相关系的编程,自然要用到C或者C++,上层的androidp平台都是用Java编写的,这个之间如何进行互动,就非常重要了。
这里提供两个思路,jni和jna,据说jna这个框架,实在底层的jni技术之上进行的一个封装,所以还是对jni技术有一个比较好的理解,才是至关重要的!!!
REF
这个作者的系列文章很给力,可参考
jni调用参考
jni调用详细参考
直接上demo吧!
package jni;
public class RecorderHandler {
static {
try{
System.out.println("load jni library start... ");
//装载对应的类库,因为我的是mac系统所以生成的是.dylib文件,如果是linux/unix生成的是.so文件
System.load("/Users/xxx/Documents/rt/libRecorderHandler.dylib");
// System.load("/root/ww/libRecorderHandler.so");
System.out.println("load jni library completed");
}catch(Exception e){
System.out.println("load jni lib occur error: " + e.getMessage());
}
}
/**
* 传入录制命令
* @param type ref enum type
* @param conf_id id
* @param conf_alias 别名
* @param force_jvb jvb
* @return
*/
public native int doRecorder(int type,String conf_id,String conf_alias,String force_jvb);
/**
* C++回调上传java
*/
public native void callback();
}
如上定义了两个native方法,第一个native方法是直接调用C++代码,第二个native是c++调用java ,声明的这个方法直接调用,我再在C++中编写调用Java部分的代码
package dubbo.service.impl;
import javax.annotation.Resource;
import jni.RecorderHandler;
import org.springframework.beans.factory.annotation.Autowired;
import recorder.service.RecorderService;
import service.RecorderResultService;
import service.TestService;
import entity.Recorder;
//dubbo暴漏接口注解的两种方式,这是一种方式,是通过注解暴漏服务,另外一种是通过配置文件xml的形式暴漏服务
//@Service
@Resource
public class RecorderServiceImpl implements RecorderService{
//用@Reference和@Autowired具有同样的效果,只是在配置文件中需要不同的配置
//@Reference
@Autowired
private TestService testService;
@Autowired
private RecorderResultService recorderResultService;
public enum Type{
RECORDSTART,RECORDSTOP,RECORDPAUSE,RECORDRESUME
}
public void startRecorder(String location) {
// TODO Auto-generated method stub
System.out.println("recorder start now....." + "location is : " + location);
//录制完成记录录制结果:
this.sendRecorderResult();
}
public void sendRecorderResult(){
try{
RecorderHandler recorderHandler = new RecorderHandler();
System.out.println("uuuu");
System.out.println("开始调用录制: " + recorderHandler.doRecorder(Type.RECORDSTART.ordinal(),"aaa","bbb","ccc"));
//执行回调
recorderHandler.callback();
}catch(Exception e){
System.out.println("occur error : "+e.getMessage());
}
}
public void recorderCallBack(String str,int i){
//这里打印成功,代表c++调用java部分成功
System.out.println("execute calll back, str is " + str +", and i is " + i );
}
}
如上是调用的接口:
如何生成.so文件呢?
首先生成头文件: .h文件,如何生成??
生成java class文件以后,利用javah jni.RecorderHandler命令进行生成
javah jni.RecorderHandler
生成对应的.h文件如下:这个文件就是定义了一些native的方法头,是不允许修改的
* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class jni_RecorderHandler */
#ifndef _Included_jni_RecorderHandler
#define _Included_jni_RecorderHandler
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jni_RecorderHandler
* Method: doRecorder
* Signature: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_jni_RecorderHandler_doRecorder
(JNIEnv *, jobject, jint, jstring, jstring, jstring);
/*
* Class: jni_RecorderHandler
* Method: callback
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jni_RecorderHandler_callback
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
编写对应的实现代码:里面包含了,对应的Java调用C++部分的代码,和C++调用Java部分的代码,,执行以下输出结果为:
#include "jni_RecorderHandler.h"
#include
#include
#include
JNIEXPORT jint JNICALL Java_jni_RecorderHandler_doRecorder
(JNIEnv *, jobject, jint, jstring, jstring, jstring)
{
return 9999;
}
JNIEXPORT void JNICALL Java_jni_RecorderHandler_callback
(JNIEnv* env, jobject)
{
jclass clazz = NULL;
jobject jobj = NULL;
jmethodID mid_construct = NULL;
jmethodID mid_instance = NULL;
jstring str_arg = NULL;
// 1、从classpath路径下搜索RecorderServiceImpl这个类,并返回该类的Class对象
clazz = env->FindClass("dubbo/service/impl/RecorderServiceImpl");
if (clazz == NULL) {
printf("can not find 'dubbo.service.impl.RecorderServiceImpl.'class");
return;
}
// 2、获取类的默认构造方法ID
mid_construct = env->GetMethodID(clazz, "","()V");
if (mid_construct == NULL) {
printf("can not find default constrct method");
return;
}
// 3、查找实例方法的ID
mid_instance = env->GetMethodID(clazz, "recorderCallBack", "(Ljava/lang/String;I)V");
if (mid_instance == NULL) {
printf(" int dubbo.service.impl.RecorderServiceImpl.class can not find method recorderCallBack");
return;
}
// 4、创建该类的实例
jobj = env->NewObject(clazz,mid_construct);
if (jobj == NULL) {
return;
}
// 5、调用对象的实例方法
str_arg = env->NewStringUTF("i am calling recorderCallBack method");
env->CallVoidMethod(jobj,mid_instance,str_arg,200);
// 删除局部引用
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(jobj);
env->DeleteLocalRef(str_arg);
}
编译命令可以保存为 complime.sh
然后赋予执行权限: chmod +x complime.sh就可以用./complime.sh执行编译了!
编译程序在mac上编译为.dylib文件
g++ -shared -I /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/include/darwin -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include jni_RecorderHandler.cpp -o libRecorderHandler.dylib
在linux文件中编译为.so文件:
g++ -shared -I /opt/jdk1.8.0_92/include -I/opt/jdk1.8.0_92/include/linux -fPIC jni_RecorderHandler.cpp -o libRecorderHandler.so
执行上面从程序,输入结果为:
如上证明Java调用C++和C++调用Java都没有问题了。