本文转自:http://codingnow.cn/cocos2d-x/992.html

文章摘要:本文主要实现两个功能: (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 */ typedef uint8_t jboolean; /* unsigned 8 bits */ typedef int8_t jbyte; /* signed 8 bits */ typedef uint16_t jchar; /* unsigned 16 bits */ typedef int16_t jshort; /* signed 16 bits */ typedef int32_t jint; /* signed 32 bits */ typedef int64_t jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #else typedef unsigned char jboolean; /* unsigned 8 bits */ typedef signed char jbyte; /* signed 8 bits */ typedef unsigned short jchar; /* unsigned 16 bits */ typedef short jshort; /* signed 16 bits */ typedef int jint; /* signed 32 bits */ typedef long long jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #endif /* "cardinal indices and sizes" */ typedef jint 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. */ typedef void * jobject; typedef jobject jclass; typedef jobject jstring; typedef jobject jarray; typedef jarray jobjectArray; typedef jarray jbooleanArray; typedef jarray jbyteArray; typedef jarray jcharArray; typedef jarray jshortArray; typedef jarray jintArray; typedef jarray jlongArray; typedef jarray jfloatArray; typedef jarray jdoubleArray; typedef jobject jthrowable; typedef jobject 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 */ 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,定义了一些常用的接口,该文件位于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 typedef struct JniMethodInfo_ { JNIEnv * env; jclass classID; jmethodID methodID; } JniMethodInfo; class CC_DLL JniHelper { public : static JavaVM* getJavaVM(); static void setJavaVM(JavaVM *javaVM); static const char * getExternalAssetPath(); static void setExternalAssetPath( const char * externalAssetPath); static jclass getClassID( const char *className, JNIEnv *env=0); static bool getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode); static bool getMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode); static std::string jstring2string(jstring str); private : static JavaVM *m_psJavaVM; static std::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 private static Handler mHandler; public static void init(Handler handler) { JniTestHelper.mHandler = handler; } public static native void setPackageName(String packageName);

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

1 2 3 4 5 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

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" 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,代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 #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值 ,内容如下:

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 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要显示的数据。

1 2 3 4 5 6 7 8 9 10 11 /** author:alexzhou email :[email protected] date :2012-12-14 **/ public class DialogMessage { public String title; public String 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 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

1 2 3 4 extern "C" { void showTipDialog( const char *title, const char *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" { 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,添加代码:

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

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

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

转载请注明来自:Alex Zhou的程序世界,本文链接:http://codingnow.cn/cocos2d-x/992.html


更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)Studio使用
  3. android activity 生命周期详解
  4. Android(安卓)Studio NDK开发案例一 JNI开发入门
  5. android xml-----Sax解析
  6. 【Android(安卓)修炼手册】Gradle 篇 -- Android(安卓)Gradle Pl
  7. Android(安卓)ndk入门准备--hello jni
  8. 关于Android加快应用崩溃效率
  9. 修改DatePickerDialog的样式

随机推荐

  1. Android中文API(125) ―― VideoView
  2. 【源码分享下载】每日更新之Android应用
  3. Android屏幕元素层次结构
  4. Android控件-多选按钮、单选按钮
  5. Android(安卓)Studio——Android(安卓)St
  6. Android:TextUtils类介绍以及常见使用
  7. android 布局文件中控件ID、name标签属性
  8. Android中获取应用程序(包)的信息-----Pa
  9. android NDK JNI
  10. Android(安卓)ImageView的scaleType属性