jni调用c++过程可以分为这几步:
1. 初始化虚拟机
2. 获取类和实例
3. 获取函数并执行
4 .销毁虚拟机
我按照以上的思想对调用java代码的过程进行c++封装, 但是在封装call函数的时候卡住了, 由于参数列表的原因,还没有想好如何封装. 这将在以后解决
首先贴出main函数代码
#include "JNIClass.h"
int main(int argc, char* argv[])
{
CJNI jni;
jni.init();
jni.getClassAndInstance("Jnitest");
jni.runMethod("output_two",NULL,2,'d',true);
return 0;
}
其中,CJNI类是对JNI接口的简单封装, init()负责初始化虚拟机, getClassAndInstance则根据传入的类名进行查找和创建实例,runMethod则根据参数调用相应的参数,这个还没有解决. main函数结束时自动销毁虚拟机
下面来看看CJNI类的定义
#include <jni.h>
#include <map>
#include <string>
using std::string;
using std::map;
#define CJNI_CREATEJVM_FAIL 0x0001 //创建虚拟机失败
#define CJNI_GETTHREAD_FAIL 0x0002 //获取线程运行环境失败
#define CJNI_GETCLASS_FAIL 0x0003 //获取类失败
#define CJNI_INITCLASS_FAIL 0x0004 //初始化类失败
#define CJNI_OK 0x0000 //运行成功
#define CJNI_RUN_FAIL 0x0005 //执行函数失败
#define CJNI_GETINFO_FAIL 0x0006 //获取信息失败
class CJNI
{
public:
CJNI();
~CJNI();
int init(); //初始化
int getClassAndInstance(const char*); //获取类对象
//int runMethod(const char*,void* = NULL,int = 0,...);
//int runStaticMethod(const char*,void* = NULL,int = 0,...);
private:
int getClassInfo(const char*);
private:
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption *options;
jclass gotClass; //当前获取的类
jobject gotInstance; //当前获取的类实例
map<string,string> methodParas; //当前类函数信息
};
CJNI类的实现
#include "JNIClass.h"
#include <stdio.h>
#include <cstdarg>
#include <string.h>
#include <iostream>
#include <stdlib.h>
using std::cout;
using std::endl;
CJNI::CJNI()
{
}
CJNI::~CJNI()
{
delete options;
jvm ->DestroyJavaVM();
}
int CJNI::init()
{
options = new JavaVMOption[2];
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=./java";
//options[2].optionString = "-verbose:jni";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
if(0 != JNI_CreateJavaVM(&jvm, (void**)&env,&vm_args))
return CJNI_CREATEJVM_FAIL;
if(0 != jvm -> AttachCurrentThread(reinterpret_cast<void**>(&env),(void*)NULL))
return CJNI_GETTHREAD_FAIL;
return CJNI_OK;
}
//@className 包名+类名
int CJNI::getClassAndInstance(const char* className)
{
//查找类
gotClass = env -> FindClass(className);
if(gotClass == 0)
return CJNI_GETCLASS_FAIL;
//初始化类
jmethodID ctor = env -> GetMethodID(gotClass,"<init>","()V");
if(0 == ctor)
return CJNI_INITCLASS_FAIL;
//创建实例
gotInstance = env -> NewObject(gotClass,ctor);
//获取函数定义
return getClassInfo(className);;
}
//@methodName 函数名
//@output 返回结果
//@num 参数个数
//@... 参数列表
int CJNI::runMethod(const char* methodName,void* output,int num,...)
{
char len[10];
sprintf(len,"%d",num);
string value = methodParas.find(len + string(methodName)) -> second;
cout << value << endl;
//查找对应函数
// char arglist[100];
// va_list argptr;
// int cnt;
// va_start(argptr, fmt);
// cnt = vsprintf(arglist, fmt, argptr);
// va_end(argptr);
// jmethodID methodID = env -> GetMethodID(gotClass,methodName,"()V");
// if(methodID == 0)
// return CJNI_RUN_FAIL;
//
// env -> CallVoidMethod(gotInstance,methodID);
// if(env ->ExceptionOccurred())
// {
// env->ExceptionDescribe();
// env->ExceptionClear();
// return 0;
// }
return CJNI_OK;
}
//@methodName 函数名
//@output 返回结果
//@num 参数个数
//@... 参数列表
int CJNI::runStaticMethod(const char* methodName,void* output,int num,...)
{
//jmethodID mid = env->GetStaticMethodID( JavaClass, "output_one", "()V");
//env -> CallStaticVoidMethod(JavaClass,mid);
return CJNI_OK;
}
//获取类中函数的信息, 调用此函数就不需要使用javap
//信息格式如下, 与javap命令产生结果一致
// Compiled from "Jnitest.java"
// public class Jnitest extends java.lang.Object{
// public Jnitest();
// Signature: ()V
// public static void output_one();
// Signature: ()V
// public void output_two();
// Signature: ()V
//}
int CJNI::getClassInfo(const char* className)
{
char cmd[256];
char line[200];
FILE* fp;
int nfunc = 0; //函数个数
string func_name, func_para;
char len[10];
sprintf(cmd, "javap -s -p java/%s", className);
fp=popen(cmd, "r");
if(fp == NULL)
return CJNI_GETINFO_FAIL;
//读取数据
while(NULL != fgets(line,200,fp))
{
int pos;
string tmp(line);
//printf(":%s",tmp.c_str());
if(string::npos != (pos = tmp.find("Signature: ")))
{
//函数参数
int lpos = tmp.find("(");
int rpos = tmp.find(")") + 1;
sprintf(len,"%d",rpos - lpos - 2);
func_para = tmp.substr(lpos,rpos - lpos + 1);
func_name = len + func_name;
methodParas.insert(map<string, string>::value_type(func_name,func_para));
}
else if(string::npos != (pos = tmp.find("(")))
{
//函数名
int rpos = pos - 1;
int lpos = tmp.find_last_of(" ", rpos) + 1;
func_name = tmp.substr(lpos,rpos - lpos + 1);
}
//memset(line,'\0',200);
}
pclose(fp);
return CJNI_OK;
}
其中runStaticMethod和runMethod没实现完整. getClassInfo实现了javap的功能,所以不需要手动去javap. 在调用init()后正常的调用函数过程为根据函数类型的不同调用对应的函数,比如静态函数调用CallStaticVoidMethod,类函数调用CallVoidMethod. 在此就不一一细讲, 文章开头贴出的链接讲的很不错了,大家可以看一看,下面贴出我用来测试流程的代码,跟链接中的代码很相似
#include <jni.h>
#include <iostream>
using std::cout;
using std::endl;
int main()
{
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
int res;
//初始化虚拟机
//设置参数
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=./java";
//options[2].optionString = "-verbose:jni";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env,&vm_args);
if(res == 0)
{
cout << "create jvm success!" << endl;
}else
{
cout << "create jvm failed!" << endl;
}
int result = jvm -> AttachCurrentThread(reinterpret_cast<void**>(&env),(void*)NULL);
if(result == 0)
{
cout << "get env success!" << endl;
}else
{
cout << "get env failed!" << endl;
}
//获取类/
jclass JavaClass;
jobject obj1;
JavaClass = env -> FindClass("Jnitest");
if(JavaClass != 0)
{
cout << "get java class success!" << endl;
//执行静态方法
jmethodID mid = env->GetStaticMethodID( JavaClass, "output_one", "()V");
env -> CallStaticVoidMethod(JavaClass,mid);
}else
{
cout << "get java class failed!" << endl;
}
///创建类对象///
jobject obj;
jmethodID ctor;
ctor = env -> GetMethodID(JavaClass,"<init>","()V");
if(ctor != 0)
{
obj = env -> NewObject(JavaClass,ctor);
cout << "get java method success!" << endl;
}else
{
cout << "get java method failed!" << endl;
}
///调用方法/
jmethodID methodID = env -> GetMethodID(JavaClass,"output_two","()V");
if(methodID != 0)
{
env -> CallVoidMethod(obj,methodID);
if(env ->ExceptionOccurred())
{
env->ExceptionDescribe();
env->ExceptionClear();
jvm ->DestroyJavaVM();
return 0;
}
}
/退出虚拟机/
jvm ->DestroyJavaVM();
return 0;
}