首页 > 编程语言 >cocos2d-x 通过JNI实现c/c++和Android的java层函数互调

cocos2d-x 通过JNI实现c/c++和Android的java层函数互调

时间:2022-12-27 16:33:06浏览次数:54  
标签:typedef java String int Object c++ env 互调 函数


文章摘要: 本文主要实现两个功能: (1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数。 (2)通过c++函数调用Android的java层函数,显示一个对话框,点击按钮退出程序。 1. 首先来简单学习一下JNI的相关知识,我这篇文章中简单实现了怎么在Android Java层调用c++函数。要想使用JNI,必须得…





本文主要实现两个功能:
(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相关的数据类型和接口。下面是相关代码片段:




​# 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结构体,封装了许多常用的函数,如:




​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源码:




​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,第二个参数是类的绝对路径,第三个参数是函数名,第四个参数是函数签名(参数和返回类型),示例代码如下:




​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++调用的函数,添加如下代码:




​private​​ ​​static​​ ​​Handler mHandler;​



 



​public​​ ​​static​​ ​​void​​ ​​init(Handler handler)​



​{​



​JniTestHelper.mHandler = handler;​



​}​



 



​public​​ ​​static​​ ​​native​​ ​​void​​ ​​setPackageName(String packageName);​




(3)打开JniTest.java,在onCreate函数中添加下面的代码:




​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




​#ifndef TEST_H​



​#define TEST_H​



 



​#endif​




test.cpp




​#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,代码如下:




​#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值 ,内容如下:




​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,修改后的值:




​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添加如下代码:


​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要显示的数据。



​/**​


​author:alexzhou​


​email :[email protected]


​date  :2012-12-14​


​**/​


 


​public​​ ​​class​​ ​​DialogMessage {​


 


​public​​ ​​String title;​


​public​​ ​​String msg;​


​}​



(10) 修改JniTest.java,添加显示对话框的函数:



​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



​extern​​ ​​"C"​


​{​


​void​​ ​​showTipDialog(​​ ​​const​​ ​​char​​ ​​*title,​​ ​​const​​ ​​char​​ ​​*msg);​


​}​



test.cpp



​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,添加代码:



​void​​ ​​exitApp()​


​{​


​CCDirector::sharedDirector()->end();​


​}​


(13)到此为止,所有代码都已经完成了,代码比较简单就不详细解释了,现在编译运行,效果如下:

李华明版本转载“

首先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函数第三个参数中传入的样式;

非静态函数是否存在,返回bool;

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. //定义Jni函数信息结构体
4. //getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
5. bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFun", "()V");
6.
7. if (!isHave) {
8. "jni:此函数不存在");
9. else{
10. "jni:此函数存在");
11. //调用此函数
12. minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID);
13. }
14. "jni-java函数执行完毕");
15. #endif

Android(Java) 代码:


 

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

运行截图:

​​

示例代码段2:

Xcode 代码:

 

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

Android(Java) 代码:


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



运行截图:

示例代码段3:


Xcode 代码:

 

1. //静态函数示例3.有参数,有返回值--------------------------------$$$$$$$-------------------------------- 
2. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
3. //定义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. "jni:此函数不存在");
11. }else{
12. "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. }
24. CCLog("jni-java函数执行完毕");
25. #endif

Android(Java) 代码:


 

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


运行截图:


  ​​​


示例代码段4:


Xcode 代码:

 

1. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
2. //定义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. "jni:此函数不存在");
8. else{
9. "jni:此函数存在");
10. //调用此函数
11. jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID,823);
12. }
13. "jni-java函数执行完毕");
14. #endif

Android(Java) 代码:


 

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


运行截图:


​​


 


 


示例代码段5:


Xcode 代码:

1. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台 
2. //定义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. "正确获取到 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. "jni-java函数执行完毕");
24. #endif

Java 代码:

 

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



运行截图:


  ​​​


 


 


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


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



标签:typedef,java,String,int,Object,c++,env,互调,函数
From: https://blog.51cto.com/u_15923385/5972924

相关文章