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" ))
{
//...
}

关于类型签名,请对照下图:
cocos2d-x 通过JNI实现c/c++和Android的java层函数互调_第1张图片

(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
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)到此为止,所有代码都已经完成了,代码比较简单就不详细解释了,现在编译运行,效果如下:
cocos2d-x 通过JNI实现c/c++和Android的java层函数互调_第2张图片

免分源码:http://download.csdn.net/detail/kaitiren/6256205





李华明版本转载“

首先Himi大概的介绍一个类 JniHelper ;

此类主要用于Jni与Java层之间的相互访问的作用,那么此类的常用的一些函数这里首先介绍下,否则直接上代码不太容易吸收 ;

JniHelper 类常用函数:

 1.  getStaticMethodInfo    (四个参数,bool 返回值)

使用示例代码:

boolisHave = 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类型!

大概说两个童鞋们应该基本理解了,那么下面详细写一些对照表,留给大家对照;

 参数、返回值样式对照表:

cocos2d-x 通过JNI实现c/c++和Android的java层函数互调_第3张图片

 

  这里的签名指的就是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中先导入如下头文件:

  
  
  
  
  1. //使用预编译,对当前平台为Android时才会导入如下头文件 
  2.   
  3. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
  4. #include <jni.h> 
  5. #include "platform/android/jni/JniHelper.h" 
  6. #include <android/log.h> 
  7. #endif 

示例代码段1:

Xcode 代码:

 

  
  
  
  
  1. ////静态函数示例1.无参数,无返回值---------------------------------$$$$$$----------------------------- 
  2. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
  3.     JniMethodInfo minfo;//定义Jni函数信息结构体 
  4.     //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数 
  5.     bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFun""()V");  
  6.   
  7.     if (!isHave) { 
  8.         CCLog("jni:此函数不存在"); 
  9.     }else
  10.         CCLog("jni:此函数存在"); 
  11.         //调用此函数 
  12.         minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID); 
  13.     } 
  14.     CCLog("jni-java函数执行完毕"); 
  15. #endif 
Android(Java) 代码:

 

  
  
  
  
  1. // 静态函数示例1.无参数,无返回值 
  2. public static void testFun() { 
  3.     Log.e("Himi""静态函数示例1.无参数,无返回值"); 

运行截图:

示例代码段2:

Xcode 代码:

 

  
  
  
  
  1. //静态函数示例2.有参数,无返回值------------------------------$$$$$$$-------------------------------- 
  2. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
  3.     JniMethodInfo minfo;//定义Jni函数信息结构体 
  4.     //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数 
  5.     bool isHave = JniHelper::getStaticMethodInfo(minfo, 
  6.                                                 "com/ai/ommr/OhMonsterMR4Android""testFunWithInt","(I)V");   
  7.   
  8.     if (!isHave) { 
  9.         CCLog("jni:此函数不存在"); 
  10.     }else
  11.         CCLog("jni:此函数存在"); 
  12.         //调用此函数 
  13.         minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,823); 
  14.     } 
  15.     CCLog("jni-java函数执行完毕"); 
  16. #endif 

Android(Java) 代码:

  
  
  
  
  1. // 静态函数示例2.有参数,无返回值 
  2. public static void testFunWithInt(int _int) { 
  3.     Log.e("Himi""静态函数示例1.有参数,无返回值;传入的参数int=" + _int); 

运行截图:

 
 
 
示例代码段3:

Xcode 代码:

 

   
   
   
   
  1. //静态函数示例3.有参数,有返回值--------------------------------$$$$$$$-------------------------------- 
  2. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
  3.     JniMethodInfo minfo;//定义Jni函数信息结构体 
  4. //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数 
  5.   
  6. bool isHave = JniHelper::getStaticMethodInfo(minfo, 
  7.                                              "com/ai/ommr/OhMonsterMR4Android","testFunWithIntAndRtn","(I)I"); 
  8. jint _int ; 
  9. if (!isHave) { 
  10.     CCLog("jni:此函数不存在"); 
  11. }else
  12.     CCLog("jni:此函数存在"); 
  13.     //调用此函数 
  14.     _int = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID,823); 
  15.   
  16.     //尝试jint是否能正常接收返回的int值 
  17.     JniMethodInfo minfo_ty; 
  18.     bool isHave = JniHelper::getStaticMethodInfo(minfo_ty, "com/ai/ommr/OhMonsterMR4Android""testFunWithInt",  "(I)V"); 
  19.      if (isHave) { 
  20.          minfo_ty.env->CallStaticVoidMethod(minfo_ty.classID, minfo_ty.methodID,_int); 
  21.      } 
  22.   
  23. CCLog("jni-java函数执行完毕"); 
  24. #endif 
Android(Java) 代码:

 

  
  
  
  
  1. // 静态函数示例3.有参数,有返回值 
  2. public static int testFunWithIntAndRtn(int _int) { 
  3.     Log.e("Himi""静态函数示例1.有参数,有返回值;传入的参数int=" + _int); 
  4.     return _int+1000; 

运行截图:

 
 
 
示例代码段4:

Xcode 代码:

 

   
   
   
   
  1. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
  2.     JniMethodInfo minfo;//定义Jni函数信息结构体 
  3.     //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数 
  4.     bool isHave = JniHelper::getStaticMethodInfo(minfo,  "com/ai/ommr/OhMonsterMR4Android","testFunWithStringAndRtn","(I)Ljava/lang/String;"); 
  5.     jobject jobj; 
  6.     if (!isHave) { 
  7.         CCLog("jni:此函数不存在"); 
  8.     }else
  9.         CCLog("jni:此函数存在"); 
  10.         //调用此函数 
  11.         jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID,823); 
  12.     } 
  13.     CCLog("jni-java函数执行完毕"); 
  14. #endif 
Android(Java) 代码:

 

  
  
  
  
  1. // 静态函数示例4.有参数,有返回值(String类型) 
  2. public static String testFunWithStringAndRtn(int _int) { 
  3.     Log.e("Himi""静态函数示例4.有参数,有返回值(String类型);int=" + _int); 
  4.     return "yes,return 'String' is OK --by Himi"

运行截图:

 
 
示例代码段5:

Xcode 代码:

 

  
  
  
  
  1. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
  2.     JniMethodInfo minfo;//定义Jni函数信息结构体 
  3.     //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数 
  4.     bool isHave = JniHelper::getStaticMethodInfo(minfo, 
  5.                                                 "com/ai/ommr/OhMonsterMR4Android",  //类的路径 
  6.                                                 "rtnActivity",   //方法名 
  7.                                                 "()Ljava/lang/Object;");   //括号里的是参数,后面的是返回值。 
  8.     jobject jobj; 
  9.     if (isHave) { 
  10.        jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID); 
  11.     } 
  12.     CCLog("正确获取到 jobj"); 
  13.   
  14.     // 
  15.     isHave = JniHelper::getMethodInfo(minfo, 
  16.                                                  "com/ai/ommr/OhMonsterMR4Android",  //类的路径 
  17.                                                  "nostaticFun",   //方法名 
  18.                                                  "()V");   //括号里的是参数,后面的是返回值。   
  19.   
  20.     if (isHave) { 
  21.          minfo.env->CallVoidMethod(jobj, minfo.methodID); 
  22.     } 
  23.      CCLog("jni-java函数执行完毕"); 
  24. #endif 
Java 代码:

 

  
  
  
  
  1. //---- 函数示例之非静态函数调用 
  2. //(先获取个对象) 
  3. public static Activity actInstance;//定义单例 
  4. public static Object rtnActivity() { 
  5.     return actInstance; 
  6. //使用此对象进行调用非静态函数 
  7. public void nostaticFun() { 
  8.     Log.e("Himi""no static Function is OK - By Himi"); 

运行截图:

 
 
 
下面来介绍一些让人棘手的多参传递问题:

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


(原文地址:http://blog.csdn.net/kaitiren/article/details/11610869)

你可能感兴趣的:(cocos2d-x)