本文主要实现两个功能:
(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,第二个参数是类的绝对路径,第三个参数是函数名,第四个参数是函数签名(参数和返回类型),示例代码如下:



​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)编译运行

运行结果:


(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 :zhoujiangbohai@163.com​


​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)到此为止,所有代码都已经完成了,代码比较简单就不详细解释了,现在编译运行,效果如下: