最近在做一个项目,主要是用到python下的一个库(sympy),之前通过java通过jython调用python,速度达不到要求,后面把python单独拿出来,做成一个本地服务,通过socket与java通信,速度上去了,但是很不方便
现在是通过c调用python,然后利用java调用c(这方面的资料网上很多,下面主要说说细节问题)1,因为java调用c,要是使用64位,所以对应的python必须下载64位版的,
2,然后在vs生成dll文件的时候,要设置为64位,我是用vs2010写的
3.注意引入python的库文件和头文件
生成dll的工程:
下面是头文件 mydll.h代码
#ifndef _MYDLL_H_
#define _MYDLL_H_
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_sympy_SympyJavaJNI_invokingSympy__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *, jobject, jstring, jstring);
JNIEXPORT jstring JNICALL Java_sympy_SympyJavaJNI_invokingSympy__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *, jobject, jstring, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif //_MYDLL_H_
注:jni.h是Jni自动生成的,奇怪的东西是jni自动生成的,直接复制过来用就是了,这里主要定义了2个接口函数,第一个参数是对应python里面的函数名,第二个参数和第三个函数是该对应函数的参数,(如果该函数只要1个参数,就是调用的第一个,利用函数重载完成),
testdlladd.cpp代码清淡,主要是实现上面的2个接口(我承认我的文件命名很奇怪)
注释中的Add函数就是c调用python中的东西,其中pDict是全局变量,后面会提到
// testdlladd.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "mydll.h"
using namespace std;
string int2str(int &);
/*
char* Add(char* a, char* b)
{
printf("pDict:%d\n",pDict);
PyObject *pFunc, *pArgs, *pRetVal;
pFunc = PyDict_GetItemString(pDict, "add");
if ( !pFunc||!PyCallable_Check(pFunc) )
{
printf("can't find function [add]");
getchar();
return 0;
}
// 参数进栈
pArgs = PyTuple_New(2);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的变量转换成一个Python对象。当需要从
// C++传递变量到Python时,就会使用这个函数。此函数
// 有点类似C的printf,但格式不同。常用的格式有
// s 表示字符串,
// i 表示整型变量,
// f 表示浮点数,
// O 表示一个Python对象。
//string a = int2str(plus1);
//string b= int2str(plus2);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s",a));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("s",b));
// 调用Python函数
pRetVal = PyObject_CallObject(pFunc, pArgs);
char* res=PyString_AsString(pRetVal);
return res;
}
*/
JNIEXPORT jstring JNICALL Java_sympy_SympyJavaJNI_invokingSympy__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv *env, jobject _obj, jstring funcName, jstring param1)
{
const char* fName = env->GetStringUTFChars(funcName,0);
const char* parameter = env->GetStringUTFChars(param1,0);
PyObject *pFunc, *pArgs, *pRetVal;
pFunc = PyDict_GetItemString(pDict, fName);
if ( !pFunc||!PyCallable_Check(pFunc) )
{
printf("can't find function %s",fName);
return 0;
}
// 参数进栈
pArgs = PyTuple_New(1);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的变量转换成一个Python对象。当需要从
// C++传递变量到Python时,就会使用这个函数。此函数
// 有点类似C的printf,但格式不同。常用的格式有
// s 表示字符串,
// i 表示整型变量,
// f 表示浮点数,
// O 表示一个Python对象。
//string a = int2str(plus1);
//string b= int2str(plus2);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s",parameter));
//PyTuple_SetItem(pArgs, 1, Py_BuildValue("s",b));
// 调用Python函数
pRetVal = PyObject_CallObject(pFunc, pArgs);
char* res=PyString_AsString(pRetVal);
env->ReleaseStringUTFChars(funcName,fName);
env->ReleaseStringUTFChars(param1,parameter);
return env->NewStringUTF(res);
}
JNIEXPORT jstring JNICALL Java_sympy_SympyJavaJNI_invokingSympy__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv * env, jobject _obj, jstring funcName, jstring param1, jstring param2)
{
const char* fName = env->GetStringUTFChars(funcName,0);
const char* p1 = env->GetStringUTFChars(param1,0);
const char* p2 = env->GetStringUTFChars(param2,0);
PyObject *pFunc, *pArgs, *pRetVal;
pFunc = PyDict_GetItemString(pDict, fName);
if ( !pFunc||!PyCallable_Check(pFunc) )
{
printf("can't find function %s",fName);
return 0;
}
// 参数进栈
pArgs = PyTuple_New(2);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的变量转换成一个Python对象。当需要从
// C++传递变量到Python时,就会使用这个函数。此函数
// 有点类似C的printf,但格式不同。常用的格式有
// s 表示字符串,
// i 表示整型变量,
// f 表示浮点数,
// O 表示一个Python对象。
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s",p1));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("s",p2));
// 调用Python函数
pRetVal = PyObject_CallObject(pFunc, pArgs);
char* res=PyString_AsString(pRetVal);
env->ReleaseStringUTFChars(funcName,fName);
env->ReleaseStringUTFChars(param1,p1);
env->ReleaseStringUTFChars(param2,p2);
return env->NewStringUTF(res);
}
string int2str(int &i) {
string s;
stringstream ss(s);
ss << i;
return ss.str();
}
stdafx.h文件代码清单
这里定义了3个全局变量,主要是因为我们要频繁调用python里的库(1秒估计要几百次吧,还没考虑高并发),所以第一次加载的时候,先把DLL文件加载的里面,避免每次调用python的时候,都去加载那个dll文件,效率就十分十分十分十分的慢
#ifndef _STDAFX_H_
#define _STDAFX_H_
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的信息
// Windows 头文件:
#include <windows.h>
#include<stdio.h>
#include "jni.h"
#include "jni_md.h"
#include <Python.h>
extern PyObject *pDict,*pName,*pModule;
#endif
下面是dllmain.cpp,dll的入口函数,我觉得该写个destroy函数
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
PyObject *pDict=NULL;
PyObject *pName=NULL;
PyObject *pModule=NULL;
static int init(void)
{
<span style="white-space:pre"> </span>// 初始化Python
//在使用Python系统前,必须使用Py_Initialize对其
//进行初始化。它会载入Python的内建模块并添加系统路
//径到模块搜索路径中。这个函数没有返回值,检查系统
//是否初始化成功需要使用Py_IsInitialized。<span style="white-space:pre"> </span>
Py_Initialize();
// 检查初始化是否成功
if ( !Py_IsInitialized() )
{
<span style="white-space:pre"> </span>printf("Py_Initialize() error");
return 0;
}
// 载入名为pytest的脚本(注意:不是pytest.py)
pName = PyString_FromString("sympyInvoke");
pModule = PyImport_Import(pName);
if ( !pModule )
{
printf("can't find pytest.py");
return 0;
}
pDict= PyModule_GetDict(pModule);
if ( !pDict )
{<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>printf("can't find Method!!");
return 0;
}
<span style="white-space:pre"> </span>
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
<span style="white-space:pre"> </span> )
{
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>switch (ul_reason_for_call)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>case DLL_PROCESS_ATTACH:init();printf("DLL in");break;
<span style="white-space:pre"> </span>case DLL_THREAD_ATTACH:
<span style="white-space:pre"> </span>case DLL_THREAD_DETACH:
<span style="white-space:pre"> </span>case DLL_PROCESS_DETACH:printf("DLL out");break;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>return TRUE;
}
至于Jni,怎么调用这个dll,网上一查一大堆,就不在赘述了!
这样生成的dll文件就可以用在我们的java工程中去啦!(注意要安装python64位)