zoukankan      html  css  js  c++  java
  • 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-r8bplatformsandroid-14arch-armusrincludejni.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 :zhoujiangbohai@163.com
    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/kaitiren/6256205

  • 相关阅读:
    数据结构开发(23):二叉树中结点的查找、插入、删除与清除操作
    数据结构开发(22):二叉树的转换、深层特性与存储结构设计
    数据结构开发(21):树中属性操作与层次遍历
    数据结构开发(20):树中结点的查找、插入、清除与删除操作
    数据结构开发(19):树的定义、操作、存储结构与实现
    数据结构开发(18):归并排序和快速排序
    数据结构开发(17):冒泡排序和希尔排序
    HTTP协议的内容协商
    长轮询实现消息推送
    XHR的应用场景
  • 原文地址:https://www.cnblogs.com/james1207/p/3318049.html
Copyright © 2011-2022 走看看