Jni 技术
Android 里面有个 jni.h 封装了 java 代码与原生代码交互的功能,cocos2dx 在cocos/platform/android/jni下面有个jniHelper 类是对 jni 的再次封装,使用 jniHelper 能很容易地实现 c++ 代码与 java 互调。
C++ 调 Java
- Android 端没什么特别要做的,只需要把函数定义好就行,函数分静态和实例两种。
- Cocos 端要注意的东西就比较多,
首先要引入相关的库,这里需要 jni.h 和 jniHelper.h 两个库,但其实 jniHelper.h 里面已经包含 jni.h 了,所以只需要包含 jniHelper.h 就行。jniHelper.h 位于
#if (CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID)
#include <platform/android/jni/JniHelper.h>
#endif
然后就是调 android 方法了,同样要加上平台判断预处理。
- 声明jni方法结构体
JniMethodInfo jm;
- 查找方法是否存在
静态方法 bool b=JniHelper::getStaticMethodInfo(jm,"完整类名“,”方法名“,”(函数参数)返回值“);
实例方法 bool b=JniHelper::getStaticMethodInfo(jm,"完整类名”,“方法名”,“(函数参数)返回值”);
完整类名是指包含完整的包名,比如 org/cocos2dx/cpp/AppActivity。
函数参数及返回值指的是类型,要写 jni 里面的类型,关于 c++ 数据类型与 jni 的对应关系,请自行上网查。
- 调用方法
根据返回值不同调用不同的函数 - 返回整型静态方法
jint jRes = jm.env->CallStaticIntMethod(jm.classID, jm.methodID, 函数参数);
- 无返回值实例方法
jm.env->CallVoidMethod(类实例, jm.methodID);
- 类实例通过调用静态方法来获得
jobject jIns = jm.env->CallStaticObjectMethod(jm.classID, jm.methodID);
Java 调 C++
- Android 声明原生函数,只声明不用实现,实现放在 C++ 端。
public [static] native 返回值 方法名(参数);
Cocos 端实现原生函数
不仅参数和返回值要和 Android 端定义的一样,函数名也有要求,函数名格式 Java_包1_包2_类名_原生函数名。比如类 Foo 有个原生函数叫 test,所在包是 org/cocos2dx/cpp,则 cocos 端实现的函数叫 Java_org_cocos2dx_cpp_Foo_testAndroid端直接调该函数。
静态方法
// Java端
public class AppActivity extends Cocos2dxActivity {
/**
* c++调java函数
* 静态方法
*/
public static int nativeCallJava(String name, String filter) {
String fullName=name+"."+filter;
javaCallNative(fullName);
return 110;
}
/**
* java调c++原生函数
* 静态方法
*/
public static native void javaCallNative(String packageName);
}
// C++端
#if CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID
//声明jni结构体
JniMethodInfo jm;
//查找java静态方法
bool b = JniHelper::getStaticMethodInfo(jm, "org/cocos2dx/cpp/AppActivity", "nativeCallJava", "(Ljava/lang/String;Ljava/lang/String;)I");
if (b)
{
//定义java字符串变量
jstring jTitle = jm.env->NewStringUTF("sound/bg");
jstring jText = jm.env->NewStringUTF("mp3");
//调用java静态方法
jint jRes = jm.env->CallStaticIntMethod(jm.classID, jm.methodID, jTitle, jText);
//资源回收
jm.env->DeleteLocalRef(jTitle);
jm.env->DeleteLocalRef(jText);
//取得java方法返回值
char name[16];
sprintf(name, "return code:%d", jRes);
this->addChild(m_tip_text);
m_tip_text->setVisible(true);
m_tip_text->setString(name);
}
#endif
#if CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID
extern "C"
{
/**
* java调原生函数
*/
void Java_org_cocos2dx_cpp_AppActivity_javaCallNative(JNIEnv* env, jobject thiz, jstring name)
{
JniMethodInfo jm;
const char* cName = jm.env->GetStringUTFChars(name, NULL);
SimpleAudioEngine::getInstance()->playBackgroundMusic(cName);
SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(100);
env->ReleaseStringUTFChars(name, cName);
}
}
#endif
实例方法
//Java端
package org.cocos2dx.cpp;
public class Foo
{
/**
* c++调java函数
* 静态方法
* 获取类实例
*/
private static Foo _ins;
public static Object getInstance() {
if (_ins == null)
{
_ins = new Foo();
}
return _ins;
}
/**
* c++调java函数
* 非静态方法
*/
public int nativeCallJava() {
javaCallNative();
return 111;
}
/**
* java调c++原生函数
* 非静态方法
*/
public native void javaCallNative();
}
//C++端
#if CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID
//声明jni结构体
JniMethodInfo jm;
//声明java类实例
jobject jIns;
//先调用静态方法获得类实例
if (JniHelper::getStaticMethodInfo(jm, "org/cocos2dx/cpp/Foo", "getInstance", "()Ljava/lang/Object;"))
{
jIns = jm.env->CallStaticObjectMethod(jm.classID, jm.methodID);
if (!jIns)
{
m_tip_text->setString("ins is null");
return;
}
}
//查找非静态方法
bool b = JniHelper::getMethodInfo(jm, "org/cocos2dx/cpp/Foo", "nativeCallJava", "()I");
if (b)
{
//调用java非静态方法
jint jRes = jm.env->CallIntMethod(jIns, jm.methodID);
jm.env->CallVoidMethod(jIns, jm.methodID);
//取得java方法返回值
char name[16];
sprintf(name, "return code:%d", jRes);
m_tip_text->setString(name);
}
#endif
#if CC_TARGET_PLATFORM==CC_PLATFORM_ANDROID
extern "C"
{
/**
* java调原生函数
*/
void Java_org_cocos2dx_cpp_Foo_javaCallNative(JNIEnv* env, jobject thiz)
{
SimpleAudioEngine::getInstance()->stopBackgroundMusic();
}
}
#endif