cocos2d 中使用jni C++ 调用 Java 方法
1.简单数据类型样例
如果我们Java中有这么一个open的静态方法,它没有參数,有一个int的返回值。怎么在C++中调用它呢?
package cb.CbCCBLE; public class CbCCBLECentralManager { public static final String TAG = "CbCCBLECentralManager Android"; public static int open() { Log.d(TAG,"open"); return 1; } }
以下就是以下详细的调用方法,难点主要就是getStaticMethodInfo方法的传入參数。
注意到cb/CbCCBLE/CbCCBLECentralManager。就是安卓的详细包名加上class名字,用中间都加'/'。
"open"就是方法的名字。最后一个是传入參数和输出參数,比較全然匹配才干找到这个java方法,括号内是输入參数,右边跟着返回值。
#if defined(ANDROID) #include "platform/android/jni/JniHelper.h" #include <jni.h> 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
參数和返回值都会用特殊简写来取代,不是int,比方I代表int。完整的參数对于例如以下:
參数类型 | 參数简写 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
表格中提到的简单类型如果是多个的话用比方是:
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");
注意下CallStaticIntMethod。由于我们调用的是静态的返回int的方法,所以用了这个。要依据调用的方法不同而使用不同的东西,详细參考: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp2556
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,所以用';'作为结束。
要注意的是CallStaticIntMethod的最后2个參数。我们传进去了一个jstring和一个jlong。什么是jstring呢?是这种:
jni有自己的数据类型,通常是j开头,用它们作为java 和 c++的中间媒体。
JNI Types | Java Type |
---|---|
void | void |
jboolean | boolean |
jbyte | byte |
jchar | char |
jshort | short |
jint | int |
jlong | long |
jfloat | float |
jdouble | double |
jobject | All Java objects |
jclass | java.lang.Class objects |
jstring | java.lang.String objects |
jobjectArray | Array of objects |
jbooleanArray | Array of booleans |
jbyteArray | Array of bytes |
jshortArray | Array of shorts |
jintArray | Array of integers |
jlongArray | Array of longs |
jfloatArray | Array of floats |
jdoubleArray | Array of doubles |
3.看一个数组样例
返回字符串的样例:
public static String[] getAllPeripherals() { Log.d(TAG,"getAllPeripherals"); String[] resultArray = {"testPeripheral1", "testPeripheral2"}; //just for test return resultArray; }
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; 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; }
数组前面要加上一个'[', 这里还用了些其它方法,像得到数组长度,依据index得到数组内容。
传入字符串的样例:
public static int scanPeripheralWithServiceUUIDs(String[] serviceUUIDs, long duration) { Log.d(TAG,"scanPeripheralWithServiceUUIDs:" + duration); }
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.看一个自己定义class的样例
package OurBLE; public class OurBlePeripheralAdvertisementData { public String deviceName; public String getDeviceName(){ return deviceName; } }
比方说有这么一个class作为jni怎样传递呢?事实上跟那个string相似。
public static OurBlePeripheralAdvertisementData getPeripheralAdvertisementData(String peripheralId) { Log.d(TAG,"getPeripheralAdvertisementData"); OurBlePeripheralAdvertisementData result = new OurBlePeripheralAdvertisementData(); result.deviceName = "deviceName1"; return result; }
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); //deviceName data.deviceName = JniHelper::jstring2string(jDeviceName); return data; }
主要这里还用了些FindClass。GetMethodID, CallObjectMethod API,这样class就能传递了。
jni真的是无所不能啊。 《cocos2d 中使用jni Java 调用 C++ 方法》