1. 程式人生 > >c++調Java走的坑

c++調Java走的坑

1.傳引數

    c++層   string str   傳引數 str.c_str();  

    jni層    const char * 

     java層 final String 

2.處理過程  jni層 先轉換,後釋放

jni的意思是java本地呼叫,通過jni可以實現java層程式碼和其他語言寫得程式碼進行互動。在cocos2d-x中,如果想要在c++層呼叫java層的程式碼,就是通過jni技術。通過呼叫java層的程式碼,我們就可以在Android平臺下實現一些引擎沒有提供給我們的功能,或者做一些其他的功能。比如加個廣告,加個分享,呼叫Android原生的對話方塊等等吧。Cocos2d-x比較人性化的是為我們封裝了jni呼叫的一些介面,這個類就是JniHelper,我們只需要使用這個類提供給我們的介面就可以完成呼叫java層程式碼的功能。

首先使用之前要包含標頭檔案,寫法如下,記住要加上條件編譯,這個東西是Android平臺下才用到。

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

2

3

#include "platform/android/jni/JniHelper.h"

4

#include <jni.h>

5

6

#endif

接著通過一小段程式碼來說明一下這個類的用法。

1

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

2

//    typedef struct JniMethodInfo_

3

//    {

4

//        JNIEnv *    env;

5

//        jclass      classID;

6

//        jmethodID   methodID;

7

//    } JniMethodInfo;

8

9

JniMethodInfo info;

10

11

//getStaticMethodInfo判斷java定義的靜態函式是否存在,返回bool

12

bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/TestJni"

,"func1","()V");

13

if(ret)

14

{

15

log("call void func1() succeed");

16

//傳入類ID和方法ID,小心方法名寫錯,第一個字母是大寫

17

info.env->CallStaticVoidMethod(info.classID,info.methodID);

18

}

19

#endif

大家書寫程式碼的時候同樣需要將程式碼使用條件編譯寫到裡面,JniMethodInfo是一個結構體,這個結構體的定義就是程式碼中註釋掉的地方,然後使用JniHelper呼叫了靜態函式getStaticMethodInfo,從它的名字就知道這個函式的作用了,就是獲得java層中靜態函式的資訊,這個資訊儲存在什麼地方呢,當然是JniMethodInfo中了,我們要獲取哪個類的哪個函式呢,第二個引數和第三個引數就是告訴JniHelper我們要獲取的是哪個函式的資訊了,第二個引數是類檔案的包名路徑,我在org/cocos2dx/cpp這個路徑下新建了一個類,叫做TestJni。其實前面的路徑就是一個包名,這裡使用的時候用/代替.。org的路徑當然就是我新建的這個工程的Android平臺目錄了。一會我要將這個專案打包然後測試一下,在eclipse下看看輸出。第三個引數當然就是方法名字了,第四個引數是需要注意的一個,有人把它叫做簽名,其實就是你要呼叫的java層函式的返回值和引數的型別說明。它把呼叫函式的引數寫到前面的括號中,返回值跟在括號的後邊,和我們平時書寫函式的時候正好相反了。那那個V是什麼東西呢,這個大寫字母就是對應的一個型別,如果是void型別,那麼就用一個V來代替,如果是一個int型別,那麼就用一個I代替,是不是很簡單,那其他的型別呢,如圖所示。

放了倆張表,用得時候查就好了,關於這個引數其他的細節問題待會討論。整個函式的返回值是一個bool型別,什麼意思不用說了吧。當這個函式的資訊存在的時候我們就進入到了if中了,然後我使用了info結構體的第一個變數來呼叫了函式CallStaticVoidMethod,這個函式可真是需要說一說。首先它的呼叫者就是儲存函式資訊的結構體JniMethodInfo的第一個成員變數env,這貨是什麼東西不用管,用就好了。然後這個函式的第一個字母是大寫,這一點要小心,Call後邊如果跟Static代表的就是我要呼叫的是一個靜態的函式,如果沒有那就不是靜態的,不是靜態的函式,我們使用JniHelper獲取資訊的時候用得就是getMethodInfo這個函式。然後Void代表的是函式的返回值,來看我們的例子,我呼叫的函式func1是一個無參無返回值的函式,這個看什麼地方,當然java程式碼我接著會向你展示,但是你可以直接看getStaticMethodInfo這個函式的第四個引數啊。這裡的這個void代表的是函式的返回值型別,所以如果呼叫的是返回值為int的java函式,那就是CallStaticIntMethod了。裡邊的引數就是結構體info的第二個和第三個成員變量了,代表的是類ID和函式ID。這樣的話基本的用法就說清楚了,接著就是TestJni中得程式碼了,我把要呼叫到得函式都寫了出來。

1

package org.cocos2dx.cpp;

2

3

import android.util.Log;

4

5

publicclass TestJni

6

{

7

publicstaticvoid func1()

8

{

9

Log.e("xiaota","java:func1,called succeed!");

10

}

11

publicstaticint func2()

12

{

13

return3838438;

14

}

15

publicstatic String func3(int i)

16

{

17

String str = "get int value:"+i;

18

Log.e("xiaota",str);

19

return str;

20

}

21

publicstatic String func4(String str)

22

{

23

Log.e("xiaota",str);

24

return str;

25

}

26

publicstaticint func5(int a,int b)

27

{

28

int c = a+b;

29

Log.e("xiaota","func5");

30

return c;

31

}

32

}

然後打包到Android平臺,我們使用USB連線上電腦,開啟eclipse,進行除錯,看看資訊輸出了沒有。

好了,這樣的話就把這個流程都說明白了,下面我們看一些細節的地方。

1

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

2

3

log("android platform!");

4

//    typedef struct JniMethodInfo_

5

//    {

6

//        JNIEnv *    env;

7

//        jclass      classID;

8

//        jmethodID   methodID;

9

//    } JniMethodInfo;

10

11

JniMethodInfo info;

12

13

//getStaticMethodInfo判斷java定義的靜態函式是否存在,返回bool

14

bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/TestJni","func1","()V");

15

if(ret)

16

{

17

log("call void func1() succeed");

18

//傳入類ID和方法ID,小心方法名寫錯,第一個字母是大寫

19

info.env->CallStaticVoidMethod(info.classID,info.methodID);

20

}

21

22

//呼叫的函式有返回值

23

ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/TestJni",

"func2","()I");

24

if(ret)

25

{

26

log("call int func2() succeed");

27

//返回的int值,用jint型別來接收

28

jint iret = info.env->CallStaticIntMethod(info.classID,info.methodID);

29

log("func2的返回值是%d",iret);

30

}

31

32

//呼叫的函式有引數有返回值,這裡有坑,注意Ljava/lang/String;後邊的;

33

ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/TestJni","

func3","(I)Ljava/lang/String;");

34

if(ret)

35

{

36

log("call int func3(int) succeed");

37

//java層的類型別對應的是jobject,把需要傳遞的引數寫到呼叫函式的後邊

38

jobject jobj = info.env->CallStaticObjectMethod(

info.classID,info.methodID,1438);

39

}

40

41

//引數和返回值都是類型別

42

ret = JniHelper::getStaticMethodInfo(info,

"org/cocos2dx/cpp/TestJni","func4","(Ljava/lang/String;)Ljava/lang/String;");

43

if(ret)

44

{

45

log("call string func4(string) succeed");

46

jobject para = info.env->NewStringUTF("haha");

47

jstring jstr = (jstring)info.env->CallStaticObjectMethod(info.classID,info.methodID,para);

48

//使用jstring2string函式將返回的jstring型別的值轉化為c++中的string型別

49

std::string text = JniHelper::jstring2string(jstr);

50

log("%s",text.c_str());

51

}

52

53

//如果函式需要的引數是倆個或者是多個,可以採用如下的寫法

54

ret = JniHelper::getStaticMethodInfo(info,

"org/cocos2dx/cpp/TestJni","func5","(II)I");

55

if(ret)

56

{

57

log("call int func5(int a,int b) succeed");

58

jint iret = info.env->CallStaticIntMethod(info.classID,info.methodID,1,2);

59

log("return value is %d",iret);

60

}

61

#endif

上邊的程式碼主要還是那倆個函式呼叫的說明,getStaticMethodInfo的第四個引數如果是類型別,注意要使用的簽名,後邊的分號也要加,如果引數有多個,直接連起來書寫就可以了。使用CallStaticMethod呼叫的時候注意一下引數和返回值的型別,傳遞引數的時候直接寫到函式的後邊,但是引數型別要正確,返回值使用對應的型別來接受,這個型別就是前面加一個j,比如java層返回的型別是int,那接受的型別就是jint,java層返回object,接受型別就是jobject。

以上是呼叫java的靜態函式,接下來是非靜態函式的呼叫。我將c++的程式碼和java的程式碼都貼出來。

1

JniMethodInfo info;

2

bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/TestJniHelper","getObj","()Ljava/lang/Object;");

3

//先獲得類的物件,然後用這個物件去呼叫它的非靜態函式

4

jobject jobj;

5

if(ret)

6

{

7

log("call static method");

8

jobj = info.env->CallStaticObjectMethod(info.classID,info.methodID);

9

}

10

//getMethodInfo判斷java定義的類非靜態函式是否存在,返回bool

11

bool re = JniHelper::getMethodInfo(info,"org/cocos2dx/cpp/TestJniHelper","func","()V");

12

if(re)

13

{

14

log("call no-static method");

15

//非靜態函式呼叫的時候,需要的是物件,所以與靜態函式呼叫的第一個引數不同

16

info.env->CallVoidMethod(jobj,info.methodID);

17

}

1

package org.cocos2dx.cpp;

2

3

import android.util.Log;

4

5

publicclass TestJniHelper

6

{

7

privatestatic TestJniHelper instance = new TestJniHelper();

8

publicstatic Object getObj()

9

{

10

return instance;

11

}

12

publicvoid func()

13

{

14

Log.e("xiaota","func is called");

15

}

16

}

因為呼叫的是非靜態的函式,所以我們使用CallVoidMethod的時候就不能傳入類ID了,要傳入一個物件,告訴它呼叫的是哪個物件的方法,所以為了有這麼一個物件,我們就得先呼叫一個靜態的方法來返回這個物件,然後用這個物件作為引數呼叫非靜態函式。好了,關於Jni的基本用就是這樣了