Cocos2d-x c++和java相互調用


一:JAVA調用C++:


c++的setNetworkState方法實現:

<span style="font-size:18px;">#ifdef __cplusplus
extern "C" {
#endif

/*</span><pre name="code" class="cpp"><span style="font-size: 18px;"><span style="white-space:pre">	</span>setNetworkState</span><span style="font-family: Arial, Helvetica, sans-serif;">方法在java中的路徑</span><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">org/cocos2dx</span><span style="font-family: Arial, Helvetica, sans-serif; font-size: 18px;">/</span><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">cpp/</span><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">AppActivity中調用,其中</span><span style="font-family: Arial, Helvetica, sans-serif; font-size: 18px;">AppActivity是類名字和文件名字</span>
*/JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_setNetworkState(JNIEnv * env, jobject thiz, jint state){log("=========Java_org_cocos2dx_cpp_AppActivity_setNetworkState============> state = %d", state);g_networkstate = state;}#ifdef __cplusplus}#endif


Java的寫法:


public class AppActivity extends Cocos2dxActivity {

	public native void setNetworkState(int state);//聲明
	@TargetApi(17)
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	// 獲取網絡狀態
		
		System.out.println("============獲取網絡狀態  01==========================");
			IntentFilter netstatefilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);


			System.out.println("============獲取網絡狀態  02==========================");
			BroadcastReceiver netstatereceiver = new BroadcastReceiver() {
				@Override
				public void onReceive(Context context, Intent intent) {


					System.out.println("============獲取網絡狀態  03==========================");
					ConnectivityManager manager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
					System.out.println("============獲取網絡狀態  04==========================");
					manager.getActiveNetworkInfo();
					System.out.println("============獲取網絡狀態  05==========================");
					boolean etherconn = false;
					boolean wificonn = false;
					boolean mobileconn = false;
					try {
						NetworkInfo etherinfo = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
						if (etherinfo.getState() == NetworkInfo.State.CONNECTED) 
							etherconn = true;
					} catch (Exception e) {
					}
					try {
						NetworkInfo wifiinfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
						if (wifiinfo.getState() == NetworkInfo.State.CONNECTED) 
							wificonn = true;
					}
					catch (Exception e) {
					}
					try {
						NetworkInfo mobileinfo = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
						if (mobileinfo.getState() == NetworkInfo.State.CONNECTED) 
							mobileconn = true;
					} catch (Exception e) {
					}
					
					int state = 0;
					if (etherconn)
						state = 1;
					else if (wificonn)
						state = 2;
					else if (mobileconn)
						state = 3;
					
					setNetworkState(state);//調用c++的
				}
			};
			registerReceiver(netstatereceiver, netstatefilter);
	}
}



二:Java調用C++

轉自:http://www.zaojiahua.com/using-jni.html


過程中要導入”Jni.h", 如果導入 "Jni.h"報錯,

把 C:\Program Files\Java\jdk1.8.0_25\include\jni.h
和 C:\Program Files\Java\jdk1.8.0_25\include\win32\jni_md.h
拷貝到
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include下

我的路徑是上面這個, 使用vs2013。你的路徑按你自己實際情況來操作。


jni的意思是java本地調用,通過jni可以實現java層代碼和其他語言寫得代碼進行交互。在cocos2d-x中,如果想要在c++層調用java層的代碼,就是通過jni技術。通過調用java層的代碼,我們就可以在Android平臺下實現一些引擎沒有提供給我們的功能,或者做一些其他的功能。比如加個廣告,加個分享,調用Android原生的對話框等等吧。Cocos2d-x比較人性化的是爲我們封裝了jni調用的一些接口,這個類就是JniHelper,我們只需要使用這個類提供給我們的接口就可以完成調用java層代碼的功能。先說一下這個類的位置,因爲自己在找的時候有點犯二,所以特意說明一下。在3.0和3.1以上的引擎版本中,這個類的位置分別如下。

Cocos2d-x利用jni調用java層代碼Cocos2d-x利用jni調用java層代碼

3.1以後引擎把原來cocos目錄下的包含各個功能的文件夾都放到了cocos目錄下,我個人認爲這樣的放法還是比較好的。就是引擎老改目錄,希望以後不要放來放去了。最主要的當然是看看怎麼使用JniHelper這個類了。

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

1 #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代替,是不是很簡單,那其他的類型呢,如圖所示。

Cocos2d-x利用jni調用java層代碼Cocos2d-x利用jni調用java層代碼

放了倆張表,用得時候查就好了,關於這個參數其他的細節問題待會討論。整個函數的返回值是一個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 public class TestJni
6 {
7     public static void func1()
8     {
9         Log.e("xiaota","java:func1,called succeed!");
10     }
11     public static int func2()
12     {
13         return 3838438;
14     }
15     public static String func3(int i)
16     {
17         String str = "get int value:"+i;
18         Log.e("xiaota",str);
19         return str;
20     }
21     public static String func4(String str)
22     {
23         Log.e("xiaota",str);
24         return str;
25     }
26     public static int func5(int a,int b)
27     {
28         int c = a+b;
29         Log.e("xiaota","func5");
30         return c;
31     }
32 }

然後打包到Android平臺,我們使用USB連接上電腦,打開eclipse,進行調試,看看信息輸出了沒有。

Cocos2d-x利用jni調用java層代碼

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

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。

Cocos2d-x利用jni調用java層代碼

以上是調用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 public class TestJniHelper
6 {
7     private static TestJniHelper instance = new TestJniHelper();
8     public static Object getObj()
9     {
10         return instance;
11     }
12     public void func()
13     {
14         Log.e("xiaota","func is called");
15     }
16 }

因爲調用的是非靜態的函數,所以我們使用CallVoidMethod的時候就不能傳入類ID了,要傳入一個對象,告訴它調用的是哪個對象的方法,所以爲了有這麼一個對象,我們就得先調用一個靜態的方法來返回這個對象,然後用這個對象作爲參數調用非靜態函數。好了,關於Jni的基本用法就是這樣了,只是本人的一點拙見,沒有了解過的可以看看,如果是大神就繞路吧。


發佈了34 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章