cocos2d-x 通过JNI实现c/c++和Android的java层函数互调 .
转载请注明来自:Alex Zhou的程序世界,本文链接:http://codingnow.cn/cocos2d-x/992.html
本文主要实现两个功能:
(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相关的数据类型和接口。下面是相关代码片段:
# 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结构体,封装了许多常用的函数,如:
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源码:
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,第二个参数是类的绝对路径,第三个参数是函数名,第四个参数是函数签名(参数和返回类型),示例代码如下:
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)编译运行,因为我是使用cygwin编译的,而且Android项目不在cocos2d-x的根目录下,所以需要修改build_native.sh,修改COCOS2DX_ROOT和NDK_MODULE_PATH的值,把当前cocos2d-x项目的路径添加到NDK_MODULE_PATH,修改后的值:
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添加如下代码:
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)到此为止,所有代码都已经完成了,代码比较简单就不详细解释了,现在编译运行,效果如下:
源码下载地址:http://download.csdn.net/detail/zhoujianghai/4890792
cocos2d-x 通过JNI实现c/c++和Android的java层函数互调 .相关推荐
- 【iOS-cocos2d-X 游戏开发之十三】cocos2dx通过Jni调用Android的Java层代码(上)
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/725.html ☞ ...
- 【iOS-cocos2d-X 游戏开发之十三】cocos2dx通过Jni调用Android的Java层代码(下)
示例代码段2: Xcode 代码: //静态函数示例2.有参数,无返回值------------------------------$$$$$$$--------------------------- ...
- JNI原理学习,美团Android面试题
1.3 Native方法注册 Native方法注册分为两种: 静态注册 多用于NDK开发 动态注册 多用于Framework层开发 下面我们用实际的例子对两种注册做区分及了解. 1.静态注册 我们在A ...
- cocos2dx jni 调用java_cocos2dx-JniHelper 使用,在c++层面调用java层接口
JNI (java native interface) 字段描述符 "([Ljava/lang/String;)V" 它是一种对函数返回值和参数的编码.这种编码叫做JNI字段描述符 ...
- Java层与Jni层的数组传递(转)
源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...
- 【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )
文章目录 I . 调用 Java 方法流程 II . 获取 jclass 对象 ( GetObjectClass ) III . 获取 jclass 对象 ( FindClass ) IV . JNI ...
- android+jni+构造函数,在JNI中调用构造函数失败Android
我想从我的JNI Android代码调用构造函数;但不知何故,它失败,以下例外.. 我相信我失去了一些非常小的东西;但我无法弄清楚......任何人都可以请指出?在JNI中调用构造函数失败Androi ...
- java层 android_Android开发实践:Java层与Jni层的数组传递
Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni ...
- JNI通过线程c回调java层的函数
1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...
最新文章
- tomcat7自身调优和JVM调优
- 清除Console口和Telnet密码教程
- 这几种程序员前途无量!你在其中吗?
- java 一切object_javaObject类
- 【Flink】kafka Response from server for which there are no in-flight requests NETWORK_EXCEPTION
- 默认conf指向位置
- php 循环 post,如何在php中使用jQuery递归调用POST循环请求
- win10系统安装提示带有gui的服务器,安装win10提示“由于技术员系统中无接入音频设备,要启动GUI”如何...
- JAVA测试人员考核_自动化测试的绩效考核
- 库存商品管理机试题(JSP)——试题讲解
- 小游戏之斗兽棋(uniapp)
- python:操作文档——TXT篇
- Java基于JSP的网络音乐KTV点歌电台网站
- 郭鹤年--亚洲糖王与酒店巨子
- Python使用selenium模块模拟登录12306
- 分享一个短视频在线去水印接口
- 高并发高流量的大型网站架构设计
- Windows12网页版开源HTML源码
- SX1301吞吐量是SX1278的多少倍?
- 猛犸优化Summary