MyException - 我的异常网
当前位置:我的异常网» 操作系统 » cocos2d-x 经过JNI实现c/c++和Android的java层函数

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

www.MyException.Cn  网友分享于:2013-09-15  浏览:41次
cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
文章摘要: 本文主要实现两个功能: (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相关的数据类型和接口。下面是相关代码片段:

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 */
typedefuint8_t         jboolean;       /* unsigned 8 bits */
typedefint8_t          jbyte;          /* signed 8 bits */
typedefuint16_t        jchar;          /* unsigned 16 bits */
typedefint16_t         jshort;         /* signed 16 bits */
typedefint32_t         jint;           /* signed 32 bits */
typedefint64_t         jlong;          /* signed 64 bits */
typedeffloat          jfloat;         /* 32-bit IEEE 754 */
typedefdouble         jdouble;        /* 64-bit IEEE 754 */
#else
typedefunsignedchar  jboolean;      /* unsigned 8 bits */
typedefsignedchar     jbyte;          /* signed 8 bits */
typedefunsignedshort jchar;         /* unsigned 16 bits */
typedefshort          jshort;         /* signed 16 bits */
typedefint            jint;           /* signed 32 bits */
typedeflonglong       jlong;          /* signed 64 bits */
typedeffloat          jfloat;         /* 32-bit IEEE 754 */
typedefdouble         jdouble;        /* 64-bit IEEE 754 */
#endif
 
/* "cardinal indices and sizes" */
typedefjint            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.
 */
typedefvoid*           jobject;
typedefjobject         jclass;
typedefjobject         jstring;
typedefjobject         jarray;
typedefjarray          jobjectArray;
typedefjarray          jbooleanArray;
typedefjarray          jbyteArray;
typedefjarray          jcharArray;
typedefjarray          jshortArray;
typedefjarray          jintArray;
typedefjarray          jlongArray;
typedefjarray          jfloatArray;
typedefjarray          jdoubleArray;
typedefjobject         jthrowable;
typedefjobject         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 */
    conststructJNINativeInterface* functions;
 
#if defined(__cplusplus)
 
    jint GetVersion()
    {returnfunctions->GetVersion(this); }
 
    jclass DefineClass(constchar*name, jobject loader, constjbyte* buf,
        jsize bufLen)
    {returnfunctions->DefineClass(this, name, loader, buf, bufLen); }
 
    jclass FindClass(constchar* name)
    {returnfunctions->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
typedefstructJniMethodInfo_
{
    JNIEnv *    env;
    jclass      classID;
    jmethodID   methodID;
} JniMethodInfo;
 
classCC_DLL JniHelper
{
public:
    staticJavaVM* getJavaVM();
    staticvoidsetJavaVM(JavaVM *javaVM);
    staticconstchar* getExternalAssetPath();
    staticvoidsetExternalAssetPath(constchar* externalAssetPath);
    staticjclass getClassID(constchar*className, JNIEnv *env=0);
    staticboolgetStaticMethodInfo(JniMethodInfo &methodinfo, constchar*className, constchar*methodName, constchar*paramCode);
    staticboolgetMethodInfo(JniMethodInfo &methodinfo, constchar*className, constchar*methodName, constchar*paramCode);
    staticstd::string jstring2string(jstring str);
 
private:
    staticJavaVM *m_psJavaVM;
    staticstd::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
privatestaticHandler mHandler;
 
publicstaticvoid init(Handler handler)
{
    JniTestHelper.mHandler = handler;
}
 
publicstaticnative void setPackageName(String packageName);

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

1
2
3
4
5
protectedvoidonCreate(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"
 
usingnamespacecocos2d;
 
extern"C"
{
 
voidJava_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv *env, jobject thiz, jstring packageName)
{
    constchar*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"
 
usingnamespacecocos2d;
 
voidsetPackageName(constchar*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
publicstaticnative void exitApp();
 
privatestaticvoid showTipDialog(finalString title, finalString text)
{
    Message msg = mHandler.obtainMessage();
    msg.what = JniTest.SHOW_DIALOG;
    DialogMessage dm = newDialogMessage();
    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 :zhoujiangbohai@163.com
date  :2012-12-14
 **/
 
publicclassDialogMessage {
 
    publicString title;
    publicString 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
publicstaticfinal int SHOW_DIALOG = 0x0001;
 
   privateHandler mHandler = newHandler()
    {
        @Override
        publicvoidhandleMessage(Message msg) {
            switch(msg.what)
            {
            caseSHOW_DIALOG:
                DialogMessage dm = (DialogMessage)msg.obj;
                newAlertDialog.Builder(JniTest.this)
                .setTitle(dm.title)
                .setMessage(dm.msg).setNegativeButton("cancle",newDialogInterface.OnClickListener() {
 
                    @Override
                    publicvoidonClick(DialogInterface dialog, intwhich) {
                        dialog.dismiss();
                    }
                })
                .setPositiveButton("Ok",
                        newDialogInterface.OnClickListener() {
 
                    @Override
                    publicvoidonClick(DialogInterface dialog, intwhich) {
                        dialog.dismiss();
                        JniTestHelper.exitApp();
                    }
                })
                .create().show();
                break;
            }
        }
    };

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

1
2
3
4
extern"C"
{
voidshowTipDialog(constchar*title, constchar*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"
{
voidshowTipDialog(constchar*title, constchar*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);
    }
}
 
voidJava_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv *env, jobject thiz, jstring packageName)
{
    constchar*pkgName = env->GetStringUTFChars(packageName, NULL);
    setPackageName(pkgName);
    env->ReleaseStringUTFChars(packageName, pkgName);
}
 
voidJava_com_alexzhou_jni_JniTestHelper_exitApp(JNIEnv *env, jobject thiz)
{
    exitApp();
}
 
}

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

1
2
3
4
voidexitApp()
{
    CCDirector::sharedDirector()->end();
}

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

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

1楼suannai0314昨天 10:42
您的文章已被推荐到博客首页和个人页侧边栏推荐文章,感谢您的分享。

文章评论

漫画:程序员的工作
漫画:程序员的工作
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
Java程序员必看电影
Java程序员必看电影
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
10个调试和排错的小建议
10个调试和排错的小建议
那些争议最大的编程观点
那些争议最大的编程观点
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员和编码员之间的区别
程序员和编码员之间的区别
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
我是如何打败拖延症的
我是如何打败拖延症的
一个程序员的时间管理
一个程序员的时间管理
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
旅行,写作,编程
旅行,写作,编程
编程语言是女人
编程语言是女人
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
如何成为一名黑客
如何成为一名黑客
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
程序员应该关注的一些事儿
程序员应该关注的一些事儿
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
中美印日四国程序员比较
中美印日四国程序员比较
代码女神横空出世
代码女神横空出世
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
 程序员的样子
程序员的样子
程序员的鄙视链
程序员的鄙视链
我的丈夫是个程序员
我的丈夫是个程序员
为什么程序员都是夜猫子
为什么程序员都是夜猫子
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
程序员必看的十大电影
程序员必看的十大电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
总结2014中国互联网十大段子
总结2014中国互联网十大段子
老程序员的下场
老程序员的下场
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有