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就能传递了。