cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
本文主要实现两个功能:
(1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数。
(2)通过c++函数调用Android的java层函数,显示一个对话框,点击按钮退出程序。
1. 首先来简单学习一下JNI的相关知识,我这篇文章中简单实现了怎么在Android Java层调用c++函数。要想使用JNI,必须得包含头文件,android是使用ndk编译c/c++的,这里jni.h文件位于:\android-ndk-r8b\platforms\android-14\arch-arm\usr\include\jni.h,该文件定义了所有和JNI相关的数据类型和接口。下面是相关代码片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
#
include <inttypes.h> /* C99 */
typedef uint8_t
jboolean; /*
unsigned 8 bits */
typedef int8_t
jbyte; /*
signed 8 bits */
typedef uint16_t
jchar; /*
unsigned 16 bits */
typedef int16_t
jshort; /*
signed 16 bits */
typedef int32_t
jint; /*
signed 32 bits */
typedef int64_t
jlong; /*
signed 64 bits */
typedef float jfloat; /*
32-bit IEEE 754 */
typedef double jdouble; /*
64-bit IEEE 754 */
#else
typedef unsigned char jboolean; /*
unsigned 8 bits */
typedef signed char jbyte; /*
signed 8 bits */
typedef unsigned short jchar; /*
unsigned 16 bits */
typedef short jshort; /*
signed 16 bits */
typedef int jint; /*
signed 32 bits */
typedef long long jlong; /*
signed 64 bits */
typedef float jfloat; /*
32-bit IEEE 754 */
typedef double jdouble; /*
64-bit IEEE 754 */
#endif
/*
"cardinal indices and sizes" */
typedef jint
jsize;
#ifdef
__cplusplus
/*
*
Reference types, in C++
*/
class _jobject
{};
class _jclass
: public _jobject
{};
class _jstring
: public _jobject
{};
class _jarray
: public _jobject
{};
class _jobjectArray
: public _jarray
{};
class _jbooleanArray
: public _jarray
{};
class _jbyteArray
: public _jarray
{};
class _jcharArray
: public _jarray
{};
class _jshortArray
: public _jarray
{};
class _jintArray
: public _jarray
{};
class _jlongArray
: public _jarray
{};
class _jfloatArray
: public _jarray
{};
class _jdoubleArray
: public _jarray
{};
class _jthrowable
: public _jobject
{};
typedef _jobject*
jobject;
typedef _jclass*
jclass;
typedef _jstring*
jstring;
typedef _jarray*
jarray;
typedef _jobjectArray*
jobjectArray;
typedef _jbooleanArray*
jbooleanArray;
typedef _jbyteArray*
jbyteArray;
typedef _jcharArray*
jcharArray;
typedef _jshortArray*
jshortArray;
typedef _jintArray*
jintArray;
typedef _jlongArray*
jlongArray;
typedef _jfloatArray*
jfloatArray;
typedef _jdoubleArray*
jdoubleArray;
typedef _jthrowable*
jthrowable;
typedef _jobject*
jweak;
#else
/* not __cplusplus */
/*
*
Reference types, in C.
*/
typedef void *
jobject;
typedef jobject
jclass;
typedef jobject
jstring;
typedef jobject
jarray;
typedef jarray
jobjectArray;
typedef jarray
jbooleanArray;
typedef jarray
jbyteArray;
typedef jarray
jcharArray;
typedef jarray
jshortArray;
typedef jarray
jintArray;
typedef jarray
jlongArray;
typedef jarray
jfloatArray;
typedef jarray
jdoubleArray;
typedef jobject
jthrowable;
typedef jobject
jweak;
#endif
/* not __cplusplus */
|
我们经常用到的是JNIEnv*,它是一个c结构体,封装了许多常用的函数,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
struct _JNIEnv
{
/*
do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface*
functions;
#if
defined(__cplusplus)
jint
GetVersion()
{ return functions->GetVersion( this );
}
jclass
DefineClass( const char *name,
jobject loader, const jbyte*
buf,
jsize
bufLen)
{ return functions->DefineClass( this ,
name, loader, buf, bufLen); }
jclass
FindClass( const char *
name)
{ return functions->FindClass( this ,
name); }
//
这里省略其他函数...
}
|
cocos2d-x引擎对jni的操作进行了封装,提供了一个非常好用的类:JniHelper,定义了一些常用的接口,该文件位于cocos2dx/platform/android/jni目录下。下面看看JniHelper.h源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
typedef struct JniMethodInfo_
{
JNIEnv
* env;
jclass
classID;
jmethodID
methodID;
}
JniMethodInfo;
class CC_DLL
JniHelper
{
public :
static JavaVM*
getJavaVM();
static void setJavaVM(JavaVM
*javaVM);
static const char *
getExternalAssetPath();
static void setExternalAssetPath( const char *
externalAssetPath);
static jclass
getClassID( const char *className,
JNIEnv *env=0);
static bool getStaticMethodInfo(JniMethodInfo
&methodinfo, const char *className, const char *methodName, const char *paramCode);
static bool getMethodInfo(JniMethodInfo
&methodinfo, const char *className, const char *methodName, const char *paramCode);
static std::string
jstring2string(jstring str);
private :
static JavaVM
*m_psJavaVM;
static std::string
m_externalAssetPath;
};
|
下面来解释JniHelper的两个常用函数:
(1)getStaticMethodInfo
用来判断Java的类静态函数是否存在,并初始化结构体JniMethodInfo,该结构体封装了JNIEnv*和java.lang.Class对象、函数ID。这样就可以使用JNIEnv*调用 CallStaticXXXMethod(jclass clazz, jmethodID methodID, …)和 CallXXXMethod(jobject obj, jmethodID methodID, …)等常用函数(XXX替换为函数返回值类型,如:Void,Int等)。
第一个参数为JniMethodInfo,第二个参数是类的绝对路径,第三个参数是函数名,第四个参数是函数签名(参数和返回类型),示例代码如下:
1
2
3
4
|
if (JniHelper::getStaticMethodInfo(t,
CLASS_NAME, "showTipDialog" , "(Ljava/lang/String;Ljava/lang/String;)V" ))
{
//...
}
|
关于类型签名,请对照下图:
(2)getMethodInfo
该函数与getStaticMethodInfo类似,用于Java类的非静态函数。
2. 下面开始实现文章开头所述的两个功能,本文是在cocos2d-x 2.0版本 自适应屏幕分辨率demo的基础上添加的。
(1)利用cocos2d-x创建一个Android工程,名为JniTest,包名为com.alexzhou.jni,此时该包下会自动生成一个JniTest.java文件。
(2)首先来实现把应用程序的包名传递给c++函数,在包下创建JniTestHelper.java,该类封装了给c++调用的函数,添加如下代码:
1
2
3
4
5
6
7
8
|
private static Handler
mHandler;
public static void init(Handler
handler)
{
JniTestHelper.mHandler
= handler;
}
public static native void setPackageName(String
packageName);
|
(3)打开JniTest.java,在onCreate函数中添加下面的代码:
1
2
3
4
5
|
protected void onCreate(Bundle
savedInstanceState){
super .onCreate(savedInstanceState);
JniTestHelper.init(mHandler);
JniTestHelper.setPackageName( this .getPackageName());
}
|
(4)java层的代码已经完成了,下面来编写jni层代码,在/jni/hellocpp/下创建test.h和test.cpp文件,test.h文件暂时不添加任何函数,代码如下:
test.h
1
2
3
4
|
#ifndef
TEST_H
#define
TEST_H
#endif
|
test.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include
"cocos2d.h"
#include
<jni.h>
#include
"platform/android/jni/JniHelper.h"
#include
"test.h"
#include
"JniTest.h"
#define
CLASS_NAME "com/alexzhou/jni/JniTestHelper"
using namespace cocos2d;
extern "C"
{
void Java_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv
*env, jobject thiz, jstring packageName)
{
const char *pkgName
= env->GetStringUTFChars(packageName, NULL);
setPackageName(pkgName);
env->ReleaseStringUTFChars(packageName,
pkgName);
}
}
|
必须加上extern “C”,声明以c语言的方式进行编译,因为c++和c在编译时生成的函数签名不一样,可以在网上查找相关资料,不然运行的时候会出现链接错误。
(5)现在编写c++函数,在Classes目录下创建JniTest.h,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#ifndef
JNI_TEST_H
#define
JNI_TEST_H
#include
"cocos2d.h"
using namespace cocos2d;
void setPackageName( const char *packageName)
{
CCLog( "packageName:
%s" ,
packageName);
}
#endif
|
(6)修改jni/Android.mk文件的LOCAL_SRC_FILES值 ,内容如下:
1
2
|
LOCAL_SRC_FILES
:= hellocpp /main .cpp
\
hellocpp /test .cpp
|
(7)编译运行,因为我是使用cygwin编译的,而且Android项目不在cocos2d-x的根目录下,所以需要修改build_native.sh,修改COCOS2DX_ROOT和NDK_MODULE_PATH的值,把当前cocos2d-x项目的路径添加到NDK_MODULE_PATH,修改后的值:
1
2
3
|
COCOS2DX_ROOT= "/cygdrive/e/cocos2d-x/cocos2d-2.0-x-2.0.4"
"NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/cocos2dx/platform/third_party/android/prebuilt:${APP_ROOT}"
|
运行结果:
(8)现在来实现通过c++函数调用java层函数,显示一个对话框。在JniTestHelper.java添加如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
public static native void exitApp();
private static void showTipDialog( final String
title, final String
text)
{
Message
msg = mHandler.obtainMessage();
msg.what
= JniTest.SHOW_DIALOG;
DialogMessage
dm = new DialogMessage();
dm.title
= title;
dm.msg
= text;
msg.obj
= dm;
msg.sendToTarget();
}
|
(9)创建一个DialogMessage.java,封装dialog要显示的数据。
1
2
3
4
5
6
7
8
9
10
11
|
/**
author:alexzhou
email
:[email protected]
date
:2012-12-14
**/
public class DialogMessage
{
public String
title;
public String
msg;
}
|
(10) 修改JniTest.java,添加显示对话框的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public static final int SHOW_DIALOG
= 0x0001 ;
private Handler
mHandler = new Handler()
{
@Override
public void handleMessage(Message
msg) {
switch (msg.what)
{
case SHOW_DIALOG:
DialogMessage
dm = (DialogMessage)msg.obj;
new AlertDialog.Builder(JniTest. this )
.setTitle(dm.title)
.setMessage(dm.msg).setNegativeButton( "cancle" , new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface
dialog, int which)
{
dialog.dismiss();
}
})
.setPositiveButton( "Ok" ,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface
dialog, int which)
{
dialog.dismiss();
JniTestHelper.exitApp();
}
})
.create().show();
break ;
}
}
};
|
(11)在test.h和test.cpp中添加显示对话框的接口:
test.h
1
2
3
4
|
extern "C"
{
void showTipDialog( const char *title, const char *msg);
}
|
test.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
extern "C"
{
void showTipDialog( const char *title, const char *msg)
{
JniMethodInfo
t;
if (JniHelper::getStaticMethodInfo(t,
CLASS_NAME, "showTipDialog" , "(Ljava/lang/String;Ljava/lang/String;)V" ))
{
jstring
jTitle = t.env->NewStringUTF(title);
jstring
jMsg = t.env->NewStringUTF(msg);
t.env->CallStaticVoidMethod(t.classID,
t.methodID, jTitle, jMsg);
t.env->DeleteLocalRef(jTitle);
t.env->DeleteLocalRef(jMsg);
}
}
void Java_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv
*env, jobject thiz, jstring packageName)
{
const char *pkgName
= env->GetStringUTFChars(packageName, NULL);
setPackageName(pkgName);
env->ReleaseStringUTFChars(packageName,
pkgName);
}
void Java_com_alexzhou_jni_JniTestHelper_exitApp(JNIEnv
*env, jobject thiz)
{
exitApp();
}
}
|
(12) 修改Classes目录下的JniTest.h,添加代码:
1
2
3
4
|
void exitApp()
{
CCDirector::sharedDirector()->end();
}
|
(13)到此为止,所有代码都已经完成了,代码比较简单就不详细解释了,现在编译运行,效果如下:
免分源码:http://download.****.net/detail/kaitiren/6256205
李华明版本转载“
首先Himi大概的介绍一个类JniHelper ;
此类主要用于Jni与Java层之间的相互访问的作用,那么此类的常用的一些函数这里首先介绍下,否则直接上代码不太容易吸收 ;
JniHelper 类常用函数:
1. getStaticMethodInfo (四个参数,bool 返回值)
使用示例代码:
bool
isHave
= JniHelper::getStaticMethodInfo(minfo,
"com/ai/ommr/OhMonsterMR4Android"
,
"testFun"
,
"()V"
);
此函数主要用于获取Java定义的类静态函数是否存在,返回bool;
此函数有3个参数:
第一参数:minfo ->JniMethodInfo 类型,后面详细说;
第二个参数:类的路径。
第三个参数:方法名(第一参数类中的函数)
第四个参数:(参数)返回类型
关于第一个和第二个参数都比较容易理解,那么第三个参数需要重点介绍一下;例如你在Java中定义一个如下函数 public void helloJni(){};
那么getStaticMethodInfo函数第三个参数你应该传入 “()V” 表示此函数无参无返回值!
如果有这么一个函数:public int helloJni(int _int){return 823};
那么getStaticMethodInfo函数第三个参数你应该传入 “(I)I” 表示此函数需要传入一个int类型的参数并且返回一个int类型!
大概说两个童鞋们应该基本理解了,那么下面详细写一些对照表,留给大家对照;
参数、返回值样式对照表:
这里的签名指的就是getStaticMethodInfo函数第三个参数中传入的样式;
2. getMethodInfo 类似与第一个函数,只是对应非静态函数;此函数主要用于获取Java定义的类非静态函数是否存在,返回bool;
JniMethodInfo类:
此类型主要用户保存类结构体,可以通过JniHelper类的getStaticMethodInfo函数实例化JniMethodInfo对象,从而使用实例的env属性调用CallStaticVoidMethod,CallVoidMethod,CallStaticIntMethod等函数进行对保存的类结构调用函数;
常用的函数如下:(静态系列函数)
1. CallStaticVoidMethod(classID,methodID);
2.CallStaticIntMethod(classID,methodID);
3~(n-1) ……省略掉,童鞋们一看就明白;
n.CallStaticObjectMethod(classID,methodID);
带参数的函数:(如int类型)
CallStaticVoidMethod(classID,methodID,int _int);
非静态系列函数:
1. CallVoidMethod(jobj,methodID);
2.CallIntMethod(jobj,methodID);
3~(n-1) ……省略掉,童鞋们一看就明白;
n.CallStaticObjectMethod(jobj,methodID);
带参数的函数:(如int类型)
CallVoidMethod(classID,methodID,int _int);
这里讲解下几个参数函数:
1. classID: 类ID ,JniMethodInfo 对象包含此属性;
2. methdID: 方法ID,JniMethodInfo 对象也包含此属性;
3. jobj : java中Object类型,如自定义类,String…
非静态函数调用的时候,需要的是对象,所以与静态函数调用的第一个参数不同;
那么既然能调用Java的函数,那就能得到函数返回值,但是这里我们得到的返回值书写类型如下:
譬如返回int 类型,在Xcode中使用 jint 这种形式,更多形式如下:
int -> jint
…省略,大家一看就懂得;
object -> jobject
估计有的童鞋已经晕了,没关系,下面Himi写了一些例子代码,结合来看就一目了然啦。
Xcode中先导入如下头文件:
- //使用预编译,对当前平台为Android时才会导入如下头文件
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)
- #include<jni.h>
- #include"platform/android/jni/JniHelper.h"
- #include<android/log.h>
- #endif
示例代码段1:
Xcode 代码:
- ////静态函数示例1.无参数,无返回值---------------------------------$$$$$$-----------------------------
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)//判断当前是否为Android平台
- JniMethodInfominfo;//定义Jni函数信息结构体
- //getStaticMethodInfo次函数返回一个bool值表示是否找到此函数
- boolisHave=JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFun","()V");
- if(!isHave){
- CCLog("jni:此函数不存在");
- }else{
- CCLog("jni:此函数存在");
- //调用此函数
- minfo.env->CallStaticVoidMethod(minfo.classID,minfo.methodID);
- }
- CCLog("jni-java函数执行完毕");
- #endif
- //静态函数示例1.无参数,无返回值
- publicstaticvoidtestFun(){
- Log.e("Himi","静态函数示例1.无参数,无返回值");
- }
运行截图:
示例代码段2:
Xcode 代码:
- //静态函数示例2.有参数,无返回值------------------------------$$$$$$$--------------------------------
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)//判断当前是否为Android平台
- JniMethodInfominfo;//定义Jni函数信息结构体
- //getStaticMethodInfo次函数返回一个bool值表示是否找到此函数
- boolisHave=JniHelper::getStaticMethodInfo(minfo,
- "com/ai/ommr/OhMonsterMR4Android","testFunWithInt","(I)V");
- if(!isHave){
- CCLog("jni:此函数不存在");
- }else{
- CCLog("jni:此函数存在");
- //调用此函数
- minfo.env->CallStaticVoidMethod(minfo.classID,minfo.methodID,823);
- }
- CCLog("jni-java函数执行完毕");
- #endif
Android(Java) 代码:
- //静态函数示例2.有参数,无返回值
- publicstaticvoidtestFunWithInt(int_int){
- Log.e("Himi","静态函数示例1.有参数,无返回值;传入的参数int="+_int);
- }
运行截图:
Xcode 代码:
- //静态函数示例3.有参数,有返回值--------------------------------$$$$$$$--------------------------------
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)//判断当前是否为Android平台
- JniMethodInfominfo;//定义Jni函数信息结构体
- //getStaticMethodInfo次函数返回一个bool值表示是否找到此函数
- boolisHave=JniHelper::getStaticMethodInfo(minfo,
- "com/ai/ommr/OhMonsterMR4Android","testFunWithIntAndRtn","(I)I");
- jint_int;
- if(!isHave){
- CCLog("jni:此函数不存在");
- }else{
- CCLog("jni:此函数存在");
- //调用此函数
- _int=minfo.env->CallStaticIntMethod(minfo.classID,minfo.methodID,823);
- //尝试jint是否能正常接收返回的int值
- JniMethodInfominfo_ty;
- boolisHave=JniHelper::getStaticMethodInfo(minfo_ty,"com/ai/ommr/OhMonsterMR4Android","testFunWithInt","(I)V");
- if(isHave){
- minfo_ty.env->CallStaticVoidMethod(minfo_ty.classID,minfo_ty.methodID,_int);
- }
- }
- CCLog("jni-java函数执行完毕");
- #endif
- //静态函数示例3.有参数,有返回值
- publicstaticinttestFunWithIntAndRtn(int_int){
- Log.e("Himi","静态函数示例1.有参数,有返回值;传入的参数int="+_int);
- return_int+1000;
- }
运行截图:
Xcode 代码:
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)//判断当前是否为Android平台
- JniMethodInfominfo;//定义Jni函数信息结构体
- //getStaticMethodInfo次函数返回一个bool值表示是否找到此函数
- boolisHave=JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFunWithStringAndRtn","(I)Ljava/lang/String;");
- jobjectjobj;
- if(!isHave){
- CCLog("jni:此函数不存在");
- }else{
- CCLog("jni:此函数存在");
- //调用此函数
- jobj=minfo.env->CallStaticObjectMethod(minfo.classID,minfo.methodID,823);
- }
- CCLog("jni-java函数执行完毕");
- #endif
- //静态函数示例4.有参数,有返回值(String类型)
- publicstaticStringtestFunWithStringAndRtn(int_int){
- Log.e("Himi","静态函数示例4.有参数,有返回值(String类型);int="+_int);
- return"yes,return'String'isOK--byHimi";
- }
运行截图:
Xcode 代码:
- #if(CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)//判断当前是否为Android平台
- JniMethodInfominfo;//定义Jni函数信息结构体
- //getStaticMethodInfo次函数返回一个bool值表示是否找到此函数
- boolisHave=JniHelper::getStaticMethodInfo(minfo,
- "com/ai/ommr/OhMonsterMR4Android",//类的路径
- "rtnActivity",//方法名
- "()Ljava/lang/Object;");//括号里的是参数,后面的是返回值。
- jobjectjobj;
- if(isHave){
- jobj=minfo.env->CallStaticObjectMethod(minfo.classID,minfo.methodID);
- }
- CCLog("正确获取到jobj");
- //
- isHave=JniHelper::getMethodInfo(minfo,
- "com/ai/ommr/OhMonsterMR4Android",//类的路径
- "nostaticFun",//方法名
- "()V");//括号里的是参数,后面的是返回值。
- if(isHave){
- minfo.env->CallVoidMethod(jobj,minfo.methodID);
- }
- CCLog("jni-java函数执行完毕");
- #endif
- //----函数示例之非静态函数调用
- //(先获取个对象)
- publicstaticActivityactInstance;//定义单例
- publicstaticObjectrtnActivity(){
- returnactInstance;
- }
- //使用此对象进行调用非静态函数
- publicvoidnostaticFun(){
- Log.e("Himi","nostaticFunctionisOK-ByHimi");
- }
运行截图:
JNI编程:c++ 调用java 对象
用C++调用Java的java.lang.String类为例:
1. Object类出创建JVM。
使用Java类之前必须要创建JVM环境。JDK由java.exe来完成。本文有Object类的静态方法BeginJVM来创建,用EndJVM来关闭。
创建JVM之后会在创建2个变量,分别是JNIEnv* env和JavaVM* jvm,JNIEnv上文已经说明,JavaVM,顾名思义,代表Java虚拟机,用它来关闭JVM。
Object类的头文件
#include "jni.h"
class Object
{
public:
static bool BeginJVM();
static bool EndJVM();
Object();
virtual ~Object();
protected:
static JNIEnv* env;
static JavaVM* jvm;
};
object.cpp代码
#include "stdafx.h"
#include "JavaClasses.h"
#include "Object.h"
Object::Object()
{}
Object::~Object()
{}
JNIEnv* Object::env=NULL;
JavaVM* Object::jvm=NULL;
//创建JVM
bool Object::BeginJVM()
{
JavaVMOption options[3];
JavaVMInitArgs vm_args;
//各种参数
options[0].optionString="-Xmx128m";
options[1].optionString="-Verbose:gc";
options[2].optionString="-Djava.class.path=.";
vm_args.version=JNI_VERSION_1_2;
vm_args.options=options;
vm_args.nOptions=3;
//创建JVM,获得jvm和env
int res = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args);
return true;
}
bool Object::EndJVM()
{
//关闭JVM
jvm->DestroyJavaVM();
return true;
}
2. C++的String类调用java.lang.String类方法
编写C++版的String类,调用java String类方法。调用的方法如下:
String replaceAll(String regex, String replacement);
boolean endsWith(String str);
int indexOf(String str);
int compareTo(String anotherString);
char charAt(int i);
String的头文件:
class String :public Object
{
public:
//与要调用的Java方法名一致。
const char * replaceAll(char *regex,char *replacement);
bool endsWith(char * str);
int indexOf(char * str);
int compareTo(char *anotherString);
char charAt(int i);
String(char *str);
virtual ~String();
};
实现:
#include "stdafx.h"
#include "String.h"
#include "jni.h"
using namespace std;
jclass clazz; //全局变量,用来传递class
jobject object; //全局变量,用来传递object
String::String(char *str)
{
jstring jstr;
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
//获得java.lang.String类
clazz=Object::env->FindClass("java/lang/String");
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
//获得String(String str)构造体
jmethodID mid= Object::env->GetMethodID(clazz,"<init>", "(Ljava/lang/String;)V");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
//将字符串封装为jstring。
jstr = Object::env->NewStringUTF(str);
if (jstr == 0) {
cerr << "Out of memory" <<endl;
exit(-1);
}
cout << "invoking method" << endl;
//创建一个java.lang.String对象。
object=Object::env->NewObject(clazz,mid,jstr);
}
String::~String()
{}
char String::charAt(int i)
{
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
//获得charAt方法,(I)C表示 参数为int型,返回char型。详细参见JNI规范
mid= Object::env->GetMethodID(clazz,"charAt", "(I)C");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jint ji=i;
cout << "invoking method" << endl;
//调用charAt
jchar z=Object::env->CallCharMethod(object,mid,i);
//返回结果。
return z;
}
int String::compareTo(char *anotherString)
{
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
//(Ljava/lang/String;)I表示参数为java.lang.String,返回int
mid= Object::env->GetMethodID(clazz,"compareTo", "(Ljava/lang/String;)I");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(anotherString);
cout << "invoking method" << endl;
//调用方法
jint z=Object::env->CallIntMethod(object,mid,jstr);
//返回结果
return z;
}
int String::indexOf(char *str)
{
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"indexOf", "(Ljava/lang/String;)I");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(str);
cout << "invoking method" << endl;
jint z=Object::env->CallIntMethod(object,mid,jstr);
return z;
}
bool String::endsWith(char *str)
{
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"endsWith", "(Ljava/lang/String;)Z");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(str);
cout << "invoking method" << endl;
bool z=Object::env->CallBooleanMethod(object,mid,jstr);
return z;
}
const char * String::replaceAll(char *regex, char *replacement)
{
if (Object::env ==NULL)
{
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jvalue array[2];
jstring jreg = Object::env->NewStringUTF(regex);
jstring jstr = Object::env->NewStringUTF(replacement);
array[0].l=jreg;
array[1].l=jstr;
cout << "invoking method" << endl;
//传入参数,调用replaceAll方法
jobject z=Object::env->CallObjectMethodA(object,mid,array);
const char *result=Object::env->GetStringUTFChars((jstring)z, 0);
return (const char *)result;
}
3.测试
编写测试代码
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
//创建JVM
Object::BeginJVM();
String test("hello");
//调用replaceAll
const char *result=test.replaceAll("l","z");
//返回结果
cout<< result <<endl;
//关闭JVM
Object::EndJVM();
}
return nRetCode;
}
4.运行
编译需要 jni.h和jvm.lib文件。
jni.h在[JAVA_HOME]\include
jvm.lib在[JAVA_HOME]\lib
运行需要jvm.dll
jvm.dll在[JAVA_HOME]\ jre\bin\client
运行结果如下:
invoking method
invoking method
hezzo
Press any key to continue