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。完整的參数对于例如以下:

參数类型參数简写
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
voidV

表格中提到的简单类型如果是多个的话用比方是:


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 TypesJava Type
voidvoid
jbooleanboolean
jbytebyte
jcharchar
jshortshort
jintint
jlonglong
jfloatfloat
jdoubledouble
jobjectAll Java objects
jclassjava.lang.Class objects
jstringjava.lang.String objects
jobjectArrayArray of objects
jbooleanArrayArray of booleans
jbyteArrayArray of bytes
jshortArrayArray of shorts
jintArrayArray of integers
jlongArrayArray of longs
jfloatArrayArray of floats
jdoubleArrayArray 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++ 方法》

http://www.waitingfy.com/archives/1648

posted on 2017-04-13 10:59  blfbuaa  阅读(208)  评论(0编辑  收藏  举报