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

来源:互联网 发布:淘宝联盟发送朋友圈 编辑:程序博客网 时间:2024/06/11 21:15
本文主要实现两个功能:
(1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数。

(2)通过c++函数调用Android的java层函数,显示一个对话框,点击按钮退出程序。


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

下面来解释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;
 
publicstaticvoidinit(Handler handler)
{
    JniTestHelper.mHandler = handler;
}
 
publicstaticnativevoidsetPackageName(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)编译运行

运行结果:

(8)现在来实现通过c++函数调用java层函数,显示一个对话框。在JniTestHelper.java添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
publicstaticnativevoidexitApp();
 
privatestaticvoidshowTipDialog(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
publicstaticfinalintSHOW_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/zhoujianghai/4890792


0 0
原创粉丝点击