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的基本用法就是这样了,只是本人的一点拙见,没有了解过的可以看看,如果是大神就绕路吧。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值