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

前端之家收集整理的这篇文章主要介绍了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相关的数据类型和接口。下面是相关代码片段:

@H_403_44@ 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
@H_502_138@ 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 */
int8_t jbyte; /* signed 8 bits */
uint16_t jchar; /* unsigned 16 bits */
int16_t jshort; /* signed 16 bits */
int32_t jint; /* signed 32 bits */
int64_t jlong; /* signed 64 bits */
@H_404_249@ typedef float jfloat; /* 32-bit IEEE 754 */
double jdouble; /* 64-bit IEEE 754 */
#else
unsigned char jboolean; /* unsigned 8 bits */
signed char jbyte; /* signed 8 bits */
short jchar; /* unsigned 16 bits */
short jshort; /* signed 16 bits */
int jint; /* signed 32 bits */
long long jlong; /* signed 64 bits */
/* 32-bit IEEE 754 */
/* 64-bit IEEE 754 */
#endif
/* "cardinal indices and sizes" */
jint jsize;
#ifdef __cplusplus
/*
* Reference types,in C++
*/
class _jobject {};
_jclass : public _jobject {};
_jstring : _jobject {};
_jarray : _jobject {};
_jobjectArray : _jarray {};
_jbooleanArray : _jarray {};
_jbyteArray : _jarray {};
_jcharArray : _jarray {};
_jshortArray : _jarray {};
_jintArray : _jarray {};
_jlongArray : _jarray {};
_jfloatArray : _jarray {};
_jdoubleArray : _jarray {};
_jthrowable : _jobject {};
_jobject* jobject;
_jclass* jclass;
_jstring* jstring;
_jarray* jarray;
_jobjectArray* jobjectArray;
_jbooleanArray* jbooleanArray;
_jbyteArray* jbyteArray;
_jcharArray* jcharArray;
_jshortArray* jshortArray;
_jintArray* jintArray;
_jlongArray* jlongArray;
_jfloatArray* jfloatArray;
_jdoubleArray* jdoubleArray;
_jthrowable* jthrowable;
_jobject* jweak;
#else /* not __cplusplus */
/*
*/
typedef void * jobject;
jobject jclass;
jobject jstring;
jobject jarray;
jarray jobjectArray;
jarray jbooleanArray;
jarray jbyteArray;
jarray jcharArray;
jarray jshortArray;
jarray jintArray;
jarray jlongArray;
jarray jfloatArray;
jarray jdoubleArray;
jobject jthrowable;
jobject jweak;
#endif /* not __cplusplus */

我们经常用到的是JNIEnv*,它是一个c结构体,封装了许多常用的函数,如:

18
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
@H_404_249@ { return functions->GetVersion( this ); }
jclass DefineClass( const *name,jobject loader, const jbyte* buf,
jsize bufLen)
functions->DefineClass( ,name,loader,buf,bufLen); }
jclass FindClass( * name)
functions->FindClass( // 这里省略其他函数...
}

cocos2d-x引擎对jni的操作进行了封装,提供了一个非常好用的类:JniHelper,定义了一些常用的接口,该文件位于cocos2dx/platform/android/jni目录下。下面看看JniHelper.h源码:

23
JniMethodInfo_
{
JNIEnv * env;
jclass classID;
jmethodID methodID;
} JniMethodInfo;
@H_404_249@ CC_DLL JniHelper
{
:
static JavaVM* getJavaVM();
static setJavaVM(JavaVM *javaVM);
const * getExternalAssetPath();
setExternalAssetPath( * externalAssetPath);
jclass getClassID( *className,JNIEnv *env=0);
static bool getStaticMethodInfo(JniMethodInfo &methodinfo,monospace!important; font-size:1em!important; padding:0px!important; background:none!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; margin:0px!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; min-height:auto!important"> *methodName,monospace!important; font-size:1em!important; padding:0px!important; background:none!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; margin:0px!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; min-height:auto!important"> *paramCode);
getMethodInfo(JniMethodInfo &methodinfo,monospace!important; font-size:1em!important; padding:0px!important; background:none!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; margin:0px!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; min-height:auto!important"> *paramCode);
std::string jstring2string(jstring str);
private :
JavaVM *m_psJavaVM;
std::string m_externalAssetPath;
};

下面来解释JniHelper的两个常用函数
(1)getStaticMethodInfo
用来判断Java的类静态函数是否存在,并初始化结构体JniMethodInfo,该结构体封装了JNIEnv*和java.lang.Class对象、函数ID。这样就可以使用JNIEnv*调用 CallStaticXXXMethod(jclass clazz,jmethodID methodID,…)和 CallXXXMethod(jobject obj,…)等常用函数(XXX替换为函数返回值类型,如:Void,Int等)。
第一个参数为JniMethodInfo,第二个参数是类的绝对路径,第三个参数是函数名,第四个参数是函数签名(参数和返回类型),示例代码如下:

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

8
private static Handler mHandler;
public static void init(Handler handler)
{
JniTestHelper.mHandler = handler;
}
@H_404_249@ native setPackageName(String packageName);

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

5
protected 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

4
#ifndef TEST_H
#define TEST_H
#endif

test.cpp

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"
@H_404_249@
using namespace cocos2d;
extern "C"
{
void Java_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv *env,jobject thiz,jstring packageName)
{
*pkgName = env->GetStringUTFChars(packageName,NULL);
setPackageName(pkgName);
env->ReleaseStringUTFChars(packageName,pkgName);
}
}

必须加上extern “C”,声明以c语言的方式进行编译,因为c++和c在编译时生成函数签名不一样,可以在网上查找相关资料,不然运行的时候会出现链接错误
(5)现在编写c++函数,在Classes目录下创建JniTest.h,代码如下:

13
#ifndef JNI_TEST_H
#define JNI_TEST_H
#include "cocos2d.h"
cocos2d;
@H_404_249@ setPackageName( *packageName)
{
CCLog( "packageName: %s" }
#endif

(6)修改jni/Android.mk文件的LOCAL_SRC_FILES值 ,内容如下:

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

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

12
exitApp();
showTipDialog( final String title, String text)
Message msg = mHandler.obtainMessage();
msg.what = JniTest.SHOW_DIALOG;
DialogMessage dm = new DialogMessage();
@H_404_249@ dm.title = title;
dm.msg = text;
msg.obj = dm;
msg.sendToTarget();
}

(9)创建一个DialogMessage.java,封装dialog要显示的数据。

11
/**
author:alexzhou
email :zhoujiangbohai@163.com
date :2012-12-14
**/
class DialogMessage {
@H_404_249@
public String title;
String msg;
}

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

33
final int SHOW_DIALOG = 0x0001 ;
private Handler mHandler = Handler()
@Override
handleMessage(Message msg) {
switch (msg.what)
@H_404_249@ {
case SHOW_DIALOG:
DialogMessage dm = (DialogMessage)msg.obj;
AlertDialog.Builder(JniTest. )
.setTitle(dm.title)
.setMessage(dm.msg).setNegativeButton( "cancle" DialogInterface.OnClickListener() {
@Override
onClick(DialogInterface dialog,153)!important; background:none!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; margin:0px!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-weight:bold!important; min-height:auto!important">int which) {
dialog.dismiss();
}
})
.setPositiveButton( "Ok" DialogInterface.OnClickListener() {
@Override
which) {
dialog.dismiss();
JniTestHelper.exitApp();
}
})
.create().show();
break ;
}
}
};

(11)在test.h和test.cpp中添加显示对话框的接口:
test.h

"C"
{
showTipDialog( *title,monospace!important; font-size:1em!important; padding:0px!important; background:none!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; margin:0px!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; min-height:auto!important"> *msg);
}

test.cpp

28
*msg)
{
JniMethodInfo t;
))
{
@H_404_249@ 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);
}
}
{
setPackageName(pkgName);
}
Java_com_alexzhou_jni_JniTestHelper_exitApp(JNIEnv *env,jobject thiz)
{
exitApp();
}
}

(12) 修改Classes目录下的JniTest.h,添加代码

exitApp()
CCDirector::sharedDirector()->end();
}

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

源码下载地址:http://download.csdn.net/detail/zhoujianghai/4890792

原文链接:https://www.f2er.com/cocos2dx/339001.html

猜你在找的Cocos2d-x相关文章