Cocos2d-x 中如何使用jni C++ 调用 Java
在 Cocos2d-x 开发中,有时需要在 C++ 代码里调用 Java 方法,借助 JNI(Java Native Interface)就能实现这一需求。下面通过不同类型的示例,详细介绍在 Cocos2d-x 里运用 JNI 从 C++ 调用 Java 方法的具体操作。
1. 简单数据类型示例
假设 Java 中有一个名为 open 的静态方法,该方法没有参数,返回值为 int 类型。那么在 C++ 里该如何调用它呢?
Java 代码
package cb.CbCCBLE;
public class CbCCBLECentralManager {
public static final String TAG = "CbCCBLECentralManager Android";
public static int open() {
Log.d(TAG, "open");
return 1;
}
}
C++ 调用代码
#if defined(ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <iostream>
int CbCCBLECentralManager::open() {
JniMethodInfo minfo;
bool isHave = JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "open", "()I");
if (!isHave) {
CCLOG("FAIL: CbCCBLECentralManager - open");
return 0;
}
int result = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID);
return result;
}
#endif
解释
getStaticMethodInfo方法的参数至关重要。"cb/CbCCBLE/CbCCBLECentralManager"是安卓的具体包名加上类名,中间用'/'分隔;"open"是方法名;"()I"表示传入参数和输出参数,括号内为输入参数(此处无输入参数),右边跟着返回值(I代表int类型)。- 参数和返回值会用特殊简写来表示,完整的参数对应关系如下:
Java 类型 JNI 简写 int I long J boolean Z byte B char C short S float F double D void V
若有多个简单类型参数,示例如下:
Java 代码
public class CbCCBLECentralManager {
public static final String TAG = "CbCCBLECentralManager Android";
public static int open(int a, int b) {
Log.d(TAG, "open");
return 1;
}
}
C++ 调用代码
JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "open", "(II)I");
注意
要根据调用方法的不同,选择合适的 CallStaticXXXMethod 方法,例如这里调用的是静态的返回 int 的方法,所以使用了 CallStaticIntMethod。具体可参考相关文档。
2. 字符串示例
字符串的处理相对复杂一些。
Java 代码
public static int scanPeripheralWithName(String name, long duration) {
Log.d(TAG, "scanPeripheralWithName name:" + name + " duration:" + duration);
return 1;
}
C++ 调用代码
int CbCCBLECentralManager::scanPeripheralWithName(std::string name, long duration) {
JniMethodInfo minfo;
bool isHave = JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "scanPeripheralWithName", "(Ljava/lang/String;J)I");
if (!isHave) {
CCLOG("FAIL: CbCCBLECentralManager - scanPeripheralWithName");
return 0;
}
jstring jname = minfo.env->NewStringUTF(name.c_str());
jlong jDuration = (long)duration;
int result = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID, jname, jDuration);
return result;
}
解释
string属于jobject类型,jobject要加L作为前缀。由于 Java 中的string类完整包名是java/lang/String,所以string的完整表示为Ljava/lang/String,并且因为是jobject,要用';'作为结束。- JNI 有自己的数据类型,通常以
j开头,它们作为 Java 和 C++ 之间的数据交互媒介。这里的jstring和jlong就是 JNI 数据类型。
3. 数组示例
返回字符串数组示例
Java 代码
public static String[] getAllPeripherals() {
Log.d(TAG, "getAllPeripherals");
String[] resultArray = {"testPeripheral1", "testPeripheral2"}; // just for test
return resultArray;
}
C++ 调用代码
std::vector<std::string> CbCCBLECentralManager::getAllPeripherals() {
std::vector<std::string> stdResult;
JniMethodInfo minfo;
bool isHave = JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "getAllPeripherals", "()[Ljava/lang/String;");
if (!isHave) {
CCLOG("FAIL: CbCCBLECentralManager - getAllPeripherals");
return stdResult;
}
jobjectArray jResult = static_cast<jobjectArray>(minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID));
jsize resultSize = minfo.env->GetArrayLength(jResult);
jsize index = 0;
while (index < resultSize) {
jstring eachElement = (jstring)minfo.env->GetObjectArrayElement(jResult, index);
std::string stdString = JniHelper::jstring2string(eachElement);
stdResult.push_back(stdString);
++index;
}
return stdResult;
}
解释
数组前面要加上一个 '['。这里还使用了 GetArrayLength 和 GetObjectArrayElement 等方法,用于获取数组长度和根据索引获取数组元素。
传入字符串数组示例
Java 代码
public static int scanPeripheralWithServiceUUIDs(String[] serviceUUIDs, long duration) {
Log.d(TAG, "scanPeripheralWithServiceUUIDs:" + duration);
return 0;
}
C++ 调用代码
int CbCCBLECentralManager::scanPeripheralWithServiceUUIDs(std::vector<std::string> serviceUUIDs, long duration) {
JniMethodInfo minfo;
bool isHave = JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "scanPeripheralWithServiceUUIDs", "([Ljava/lang/String;J)I");
if (!isHave) {
CCLOG("FAIL: CbCCBLECentralManager - scanPeripheralWithServiceUUIDs");
return 0;
}
jint size = serviceUUIDs.size();
jclass StringObject = minfo.env->FindClass("java/lang/String");
jobjectArray jServiceUUIDsArray = minfo.env->NewObjectArray(size, StringObject, NULL);
jlong jDuration = (long)duration;
for (int i = 0; i < serviceUUIDs.size(); i++) {
jstring serviceUUID = minfo.env->NewStringUTF(serviceUUIDs[i].c_str());
minfo.env->SetObjectArrayElement(jServiceUUIDsArray, i, serviceUUID);
}
int result = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID, jServiceUUIDsArray, jDuration);
return result;
}
4. 自定义类示例
Java 代码
package OurBLE;
public class OurBlePeripheralAdvertisementData {
public String deviceName;
public String getDeviceName() {
return deviceName;
}
}
public static OurBlePeripheralAdvertisementData getPeripheralAdvertisementData(String peripheralId) {
Log.d(TAG, "getPeripheralAdvertisementData");
OurBlePeripheralAdvertisementData result = new OurBlePeripheralAdvertisementData();
result.deviceName = "deviceName1";
return result;
}
C++ 调用代码
CbCCBLEPeripheralAdvertisementData CbCCBLECentralManager::getPeripheralAdvertisementData(std::string peripheralId) {
CbCCBLEPeripheralAdvertisementData data = CbCCBLEPeripheralAdvertisementData();
JniMethodInfo minfo;
bool isHave = JniHelper::getStaticMethodInfo(minfo, "cb/CbCCBLE/CbCCBLECentralManager", "getPeripheralAdvertisementData", "(Ljava/lang/String;)LOurBLE/OurBlePeripheralAdvertisementData;");
if (!isHave) {
CCLOG("FAIL: CbCCBLECentralManager - getPeripheralAdvertisementData");
return data;
}
jstring jPeripheralId = minfo.env->NewStringUTF(peripheralId.c_str());
jobject result = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID, jPeripheralId);
jclass CbCCBLEPeripheralAdvertisementDataClass = minfo.env->FindClass("OurBLE/OurBlePeripheralAdvertisementData");
jmethodID deviceNameMId = minfo.env->GetMethodID(CbCCBLEPeripheralAdvertisementDataClass, "getDeviceName", "()Ljava/lang/String;");
jstring jDeviceName = (jstring)minfo.env->CallObjectMethod(result, deviceNameMId);
data.deviceName = JniHelper::jstring2string(jDeviceName);
return data;
}
解释
这里使用了 FindClass、GetMethodID 和 CallObjectMethod 等 API,从而实现了自定义类的传递。
总之,JNI 功能强大,能实现 C++ 和 Java 之间的交互。若想了解 Java 调用 C++ 的相关内容,可参考文章 《Cocos2d-x 中使用 jni Java 调用 C++ 方法》。