您的位置:首页 > 移动开发 > Cocos引擎

Cocos2d-x3.3RC0通过JNI实现Java与C++互调

2014-10-30 02:53 393 查看

一、JNI

JNI(Java Native Interface):Java的本地调用。本文通过JNI在Cocos2d-x3.3RC0中完成Java与C++的互调。具体实现以下两个功

能:(1)通过Android sdk的API得到应用程序的包名,并传递给C++层函数。(2)通过C++函数调用Android的Java层函数,显示一个对话框。点击按钮退出程序。

详细知识见:http://blog.csdn.net/yuxikuo_1/article/details/39577257。其中最重要的是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类解决Java与C++的通信。

下面介绍两个常用的函数:

1、getStaticMethodInfo:

        用来判断java类中静态函数是否存在,初始化结构体JniMethodInfo。该结构体封装了JNIEnv*和java.lang.Class、函数ID。这样可以使用JNIEnv*调用CallStaticXXXMethod(jclass clazz,jmethodID methodID,...)和CallXXXMethod(jobject obj,jmethodID
methodID,...)等常用函数,其中XXX代表函数返回值类型,如void、int等。如下代码:参数1:JniMethodInfo,参数2:类的绝对路径,该路径为:proj.android/src/下的目录,例如引擎模板工程下的路径为:src/org/cocos2dx/cpp/XXX。XXX为cpp下的java文件。记住路径中不用加.java后缀,因为路径使用的是类名。参数3:函数名,参数4:函数签名,具体规则见3类型签名

JniMethodInfo info;
bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/AppActivity","getObj","()Ljava/lang/Object;");
jobject jobj;
if(ret)
{
log("call void getObj() succeed");
jobj = info.env->CallStaticObjectMethod(info.classID,info.methodID);
}

bool re = JniHelper::getMethodInfo(info,"org/cocos2dx/cpp/AppActivity","func1","()V");
if(re)
{
log("call func1 succeed");
info.env->CallVoidMethod(jobj,info.methodID);
}

2、getMethodInfo:用于调用Java类的非静态函数

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo info;
//判断org/cocos2dx/cpp/AppActivity.java中是否存在getObj静态函数
bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/AppActivity","getObj","()Ljava/lang/Object;");
jobject jobj;//用于存放返回的对象
if(ret)
{
log("call void getObj() succeed");
jobj = info.env->CallStaticObjectMethod(info.classID,info.methodID);//调用getObj函数,返回一个对象
}
//判断org/cocos2dx/cpp/AppActivity.java中是否存在func1非静态函数
bool re = JniHelper::getMethodInfo(info,"org/cocos2dx/cpp/AppActivity","func1","()V");
if(re)
{
log("call func1 succeed");
info.env->CallVoidMethod(jobj,info.methodID);//通过返回的对象调用非静态函数
}

#endif

3、类型签名

类型签名Java类型
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
L full-qualified-class;完全限定的类
[ typetype[ ]
(arg-types) ret-type方法类型
        如java方法:long f(int n,String s,int[] arr); 类型签名为:(ILjava/lang/String;[I)J。注意L后的分号,[是半开的,要与类型签名完全一致。

二、具体步骤

1、创建Cocos2d-x3.3RC0工程

这个不做过多介绍,既然研究到Jni了,相比都不是太菜鸟了。

2、ADT与XCode分别导入工程

3、Xcode的Class目录下添加JniTest类



JniTest.h代码如下:JniTest.cpp暂时没有代码
#ifndef __JniDemo__JniTest__
#define __JniDemo__JniTest__

#include "cocos2d.h"
USING_NS_CC;
//定义两个C++方法,在Jni的test.h中被Java方法调用。

//该方法被Java_org_cocos2dx_cpp_JniTestHelper_setPackageName调用
//定义org/cocos2dx/cpp/JniTestHelper类中的setPackageName方法
void setPackageName(const char* packageName)
{
log("packageName = %s",packageName);
}
//该方法被Java_org_cocos2dx_cpp_JniTestHelper_exitApp调用
//定义org/cocos2dx/cpp/JniTestHelper类中得exitApp函数
void exitApp()
{
Director::getInstance()->end();
}

#endif /* defined(__JniDemo__JniTest__) */


然后在HelloWorldScene.cpp中包含如下头文件,并在menuCloseCallback中添加如下代码:

//头文件包含,判断平台
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "../proj.android/jni/hellocpp/test.h"//一定是相对路径
#endif

//调用C++调用Java层代码
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
showTipDialog("exit","Exit,Really Go?");
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}

4、Jni层代码

打开ADT工程目录下的jni/hellocpp/列表,在hellocpp下添加c++类,test.cpp和test.h。代码如下:一定包含extern "C"

</pre><p class="p1"><pre name="code" class="cpp">#ifndef TEST_H_
#define TEST_H_
extern "C"
{
//C++调Java的函数接口,该方法在HelloWorldScene中menuCallback函数中使用。
void showTipDialog(const char* title,const char* msg);
}
#endif


test.cpp代码如下:

#include "test.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#include "../../../Classes/JniTest.h"
#include <jni.h>

#define CLASS_NAME "org/cocos2dx/cpp/JniTestHelper"
using namespace cocos2d;
extern "C"
{
//C++调Java的函数,在HelloWorldScene中的menuCallback函数中调用。
void showTipDialog(const char* title,const char* msg)
{
JniMethodInfo t;
//判断CLASS_NAME的类中是否存在showTipDialog函数,如果存在,则调用。
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);
}
}
//Java调C++函数,Java函数来自org/cocos2dx/cpp/JniTestHelper的java类。同时该函数为JniTestHelper的java类中setPackageName原生方法的定义
void Java_org_cocos2dx_cpp_JniTestHelper_setPackageName(JNIEnv* env,jobject thiz,jstring packageName)
{
const char* pkgName = env->GetStringUTFChars(packageName,NULL);
setPackageName(pkgName);//此处调用的是Class目录下JniTest类中的函数
env->ReleaseStringUTFChars(packageName,pkgName);
}
//同上,该函数定义了JniTestHelper的java类中的exitApp原生函数。
void Java_org_cocos2dx_cpp_JniTestHelper_exitApp(JNIEnv* env,jobject thiz)
{
exitApp();//此处调用的时Class目录中JniTest类中的函数。
}
}

5、Java层函数

在ADT工程目录src/org.cocos2dx.cpp的目录下添加java类,JniTestHelper.java

1)JniTestHelper.java代码如下:

#include "test.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#include "../../../Classes/JniTest.h"
#include <jni.h>

#define CLASS_NAME "org/cocos2dx/cpp/JniTestHelper"
using namespace cocos2d;
extern "C"
{
//C++调Java的函数,在HelloWorldScene中的menuCallback函数中调用。
void showTipDialog(const char* title,const char* msg)
{
JniMethodInfo t;
//判断CLASS_NAME的类中是否存在showTipDialog函数,如果存在,则调用。
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);
}
}
//Java调C++函数,Java函数来自org/cocos2dx/cpp/JniTestHelper的java类。同时该函数为JniTestHelper的java类中setPackageName原生方法的定义
void Java_org_cocos2dx_cpp_JniTestHelper_setPackageName(JNIEnv* env,jobject thiz,jstring packageName)
{
const char* pkgName = env->GetStringUTFChars(packageName,NULL);
setPackageName(pkgName);//此处调用的是Class目录下JniTest类中的函数
env->ReleaseStringUTFChars(packageName,pkgName);
}
//同上,该函数定义了JniTestHelper的java类中的exitApp原生函数。
void Java_org_cocos2dx_cpp_JniTestHelper_exitApp(JNIEnv* env,jobject thiz)
{
exitApp();//此处调用的时Class目录中JniTest类中的函数。
}
}


2)AppActivity.java代码

package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
import org.cocos2dx.lib.Cocos2dxHandler.DialogMessage;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class AppActivity extends Cocos2dxActivity{
public static final int SHOW_DIALOG = 0x0001;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
JniTestHelper.init(mHandler);
//调用JniTestHelper的setPackageName函数,setPackageName函数在test.cpp中定义,最终调用C++层的setPackageName方法
JniTestHelper.setPackageName(this.getPackageName());
}

public Cocos2dxGLSurfaceView onCreateView(){
Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
glSurfaceView.setEGLConfigChooser(5,6,5,0,16,8);
return glSurfaceView;
}
static {
System.loadLibrary("cocos2dcpp");
}

private Handler mHandler = new Handler()
{
public void handleMessage(Message msg) {

switch (msg.what)
{
case SHOW_DIALOG:
DialogMessage dm = (DialogMessage)msg.obj;
new AlertDialog.Builder(AppActivity.this)
.setTitle(dm.titile)
.setMessage(dm.message).setNegativeButton("cancle", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
arg0.dismiss();
}
})
.setPositiveButton("Ok",new DialogInterface.OnClickListener(){
public void onClick(DialogInterface arg0, int arg1) {
arg0.dismiss();
JniTestHelper.exitApp();//调用JniTestHelper的exitApp函数,exitApp函数在test.cpp中定义,最终调用C++层的exitApp方法
}
})
.create().show();
break;
}
}
};
}


好了,代码和注释基本就结束了,还需要该的是Android.mk文件。代码如下:

LOCAL_SRC_FILES := hellocpp/main.cpp \
hellocpp/test.cpp \   //将新建的test.cpp类加入mk文件
../../Classes/AppDelegate.cpp \
../../Classes/HelloWorldScene.cpp
之前介绍过万能mk文件生成方法,详见http://blog.csdn.net/yuxikuo_1/article/details/39552431。为了减少出问题的几率,建议改高AndroidManifest中的SDK版本,不改无所谓也。

6、工程总目录





三、编译运行

如出现问题可参考
1)http://blog.csdn.net/yuxikuo_1/article/details/39654499
2)http://blog.csdn.net/yuxikuo_1/article/details/39552639
3)http://blog.csdn.net/yuxikuo_1/article/details/39671733

注:环境Mac  XCode6  ADT22.2.1  Cocos2d-x3.3RC0  红米Note。



四、源码

说了这么多,没有源码那不是坑爹么。源码连接:http://pan.baidu.com/s/1jGn80QE
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Cocos2d-x JNI Java C++