[cocos2dx]在cocos2dx中通过Jni实现Java与C++的互相调用二

jni详解介绍

JNI是JVM实现中的一部分,因此Native语言和Java代码都运行在JVM的宿主环境。JNI的出现使得开发者既可以利用Java语言跨平台、类库丰 富、开发便捷等特点,又可以利用Native语言的高效。
JNI是一个双向的接口:开发者不仅可以通过JNI在Java代码中访问Native模块,还可以在 Native代码中嵌入一个JVM,并通过JNI访问运行于其中的Java模块。可见,JNI担任了一个桥梁的角色,它将JVM与Native模块联系起来,从而实现了Java代码与Native代码的互访。如下图:

缺点:由于Native模块的使用,Java代码会丧失其原有的跨平台性和类型安全等特性。但是这不是我们应该担心的,不是吗?哈哈。也就是说,JNI是帮助游戏在Java代码调用Native接口和在Native代码调用Java接口。

方法1. 基本使用

c++接口, 一般来说,要在Native代码中访问Java对象,有如下几个步骤:
  • 得到该Java对象的类定义。JNI定义了jclass 这个类型来表示Java的类的定义,并提供了FindClass接口,根据类的完整的包路径即可得到其jclass 。
  • 根据jclass 创建相应的对象实体,即jobject 。在Java中,创建一个新对象只需要使用new 关键字即可,但在Native代码中创建一个对象则需要两步:首先通过JNI接口getmethodID得到该类的构造函数,然后利用NewObject接口构造出该类的一个实例对象。
  • 访问jobject 中的成员变量或方法。访问对象的方法是先得到方法的Method ID,然后使用CallMethod 接口调用,这里Type对应相应方法的返回值——返回值为基本类型的都有相对应的接口,如CallIntMethod;其他的返回值(包括String) 则为CallObjectMethod。可以看出,创建对象实质上是调用对象的一个特殊方法,即构造函数。访问成员变量的步骤一样:首先 GetFieldID得到成员变量的ID,然后Get/SetField读/写变量值。

方法2. jnihelper

2dx里面为我们提供了一个JniHelper类,来满足与Java层的数据交互,JniHelper可以很方便的调用java层的动静态方法

C++调用Java

JniUtil.h

#pragma  once
#include <string>
using namespace std;
namespace JniUtil
{
	string callJava_getAppVersion();
	bool callJava_copyText(string copyText);
	string callJava_getTestAllString(bool b,int i,float f,double d,string s);
	void callJava_callNativeFunshowtext(bool b,string s);
}

JniUtil.cpp

#include "JniUtil.h"
#include "cocos2d.h"

#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include <Jni.h>
#include "platform/android/jni/JniHelper.h"
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "IosHelper.h"
#endif

#define JAVA_CLASSNAME  "org/cocos2dx/cpp/AppActivity"
using namespace cocos2d;
namespace JniUtil
{
	string callJava_getAppVersion()
	{
		string str = "";
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		JniMethodInfo minfo; 
		//在org.cocos2dx.cpp.AppActivity文件中查找static String getAppVersion();这静态方法是否存在。
		bool isHave = JniHelper::getStaticmethodInfo(minfo,JAVA_CLASSNAME,"getAppVersion","()Ljava/lang/String;");
		if (isHave)  
		{  
	
			jstring jVersion = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID,minfo.methodID); 
			
			//使用jstring2string函数将返回的jstring类型的值转化为c++中的string类型
            //string text = JniHelper::jstring2string(jVersion);

			const char* version = minfo.env->GetStringUTFChars(jVersion,0);
			str = version;
			minfo.env->ReleaseStringUTFChars(jVersion,version);
			minfo.env->DeleteLocalRef(minfo.classID);  
			cocos2d::log("JniFun call callJava_getAppVersion over!===%s",str.c_str());
		}  
		else
		{
			cocos2d::log("JniFun call callJava_getAppVersion error!");
		}
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		//IosHelper::HuiPay(kStr.c_str());
#endif
		return str;
	}
	bool callJava_copyText(string copyText)
	{
		bool isSuccess = false;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		JniMethodInfo minfo;  
		bool isHave = JniHelper::getStaticmethodInfo(minfo,"copyText","(Ljava/lang/String;)Z");
		if (isHave)  
		{  
			jstring jcopyText = minfo.env->NewStringUTF(copyText.c_str());
			jboolean jIsSuccess= minfo.env->CallStaticBooleanMethod(minfo.classID,minfo.methodID,jcopyText); 
			isSuccess = jIsSuccess;
			minfo.env->DeleteLocalRef(minfo.classID);  
			cocos2d::log("JniFun call callJava_copyText over!");
		}  
		else
		{
			cocos2d::log("JniFun call callJava_copyText error!");
		}
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		//IosHelper::HuiPay(kStr.c_str());
#endif
		return isSuccess;
	}
	string callJava_getTestAllString(bool b,string s)
	{
		string str = "";
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		JniMethodInfo minfo;  
		bool isHave = JniHelper::getStaticmethodInfo(minfo,"getTestAllString","(ZIFDLjava/lang/String;)Ljava/lang/String;");
		if (isHave)  
		{  
			jstring js = minfo.env->NewStringUTF(s.c_str());
			jstring jRes = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID,b,i,f,d,js); 
			const char* res = minfo.env->GetStringUTFChars(jRes,0);
			str = res;
			minfo.env->ReleaseStringUTFChars(jRes,res);
			minfo.env->DeleteLocalRef(js);
			minfo.env->DeleteLocalRef(minfo.classID);  
			cocos2d::log("JniFun call callJava_getTestAllString over!==%s",str.c_str());
		}  
		else
		{
			cocos2d::log("JniFun call callJava_getTestAllString error!");
		}
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		//IosHelper::HuiPay(kStr.c_str());
#endif
		return str;
	}
	void callJava_callNativeFunshowtext(bool b,string s)
	{
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		JniMethodInfo minfo;  
		bool isHave = JniHelper::getStaticmethodInfo(minfo,"callNativeFunshowtext","(ZIFDLjava/lang/String;)V");
		if (isHave)  
		{  
			jstring js = minfo.env->NewStringUTF(s.c_str());
			jstring jRes = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID,0);
			string str = res;
			minfo.env->ReleaseStringUTFChars(jRes,res);
			minfo.env->DeleteLocalRef(js);
			minfo.env->DeleteLocalRef(minfo.classID);  
			cocos2d::log("JniFun call callJava_callNativeFunshowtext over!==%s",str.c_str());
		}  
		else
		{
			cocos2d::log("JniFun call callJava_callNativeFunshowtext error!");
		}
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		//IosHelper::HuiPay(kStr.c_str());
#endif
	}
}

Java调用C++

AppActivity.java
package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxHelper;

import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.util.AndroidException;
import android.view.WindowManager;

public class AppActivity extends Cocos2dxActivity 
//public class AppActivity extends Cocos2dxHelper 
{
	//在java类中定义一个方法,用于提供给java调用C++
	public static native void NativeFunshowtext(String text);
	
	private static AppActivity appActivity = null;
    //剪切板管理工具类
    private static ClipboardManager mClipboardManager;
    //剪切板Data对象
    private static ClipData mClipData;
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		appActivity = this;
		getwindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
	}
	
	public static String getAppVersion() throws AndroidException
	{
		PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(),0);
		String version = pInfo.versionName +" "+ pInfo.versionCode;
		return version;
	}
	public static boolean copyText(String copyTxt)
	{
        //创建一个新的文本clip对象
        mClipData = ClipData.newPlainText("Simple test",copyTxt);
		//把clip对象放在剪贴板中
        mClipboardManager.setPrimaryClip(mClipData);
        return true;
	}
	public static String getTestAllString(boolean b,String s)
	{
		final String str = "bool:"+ b + " int:" + i + " float:" + f + " double:" + d + " String:" + s;
		System.out.println("----getTestAll----out runOnUiThread-----"+str);
		//添加到主线程
		appActivity.runOnUiThread(new Runnable(){
			public void run(){
				System.out.println("----getTestAll----in runOnUiThread-----"+str);				
			}
		});
		return str;
	}
	public static void callNativeFunshowtext(boolean b,String s)
	{
		final String str = "bool:"+ b + " int:" + i + " float:" + f + " double:" + d + " String:" + s;
		System.out.println("----callNativeFunshowtext----out runOnGLThread-----"+str);
		//想从java代码来改变cocos2dxUI界面,需要在GL线程中运行,否则会崩溃
		appActivity.runOnGLThread(new Runnable(){
			public void run(){
				System.out.println("----callNativeFunshowtext----in runOnGLThread-----"+str);
				NativeFunshowtext(str);
			}
		});
	}	
}
JniCallback.h
#pragma  once

namespace JniCallback
{

}
JniCallback.cpp
#include "JniCallback.h"
#include "cocos2d.h"

#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include <Jni.h>
#include "platform/android/jni/JniHelper.h"
#endif

using namespace cocos2d;

namespace JniCallback
{
	extern "C" 
	{
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
        //Java_:是格式,必须加的
		//org_cocos2dx_cpp_AppActivity_NativeFunshowtext:是包名+类名+方法名		
		JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_NativeFunshowtext (jnienv* env,jclass method,jstring param)
		{
			const char* data = env->GetStringUTFChars(param,0);
			cocos2d::log("Java_org_cocos2dx_cpp_AppActivity_NativeFunshowtext---- :%s",data);
			//do cocosUI something
			env->ReleaseStringUTFChars(param,data);
		}
#endif
	}
}

跟jni相关的C++代码文件放在proj.android\jni\hellocpp目录下,每加一个cpp文件,都需在proj.android\jni的Andriod.mk文件添加:
LOCAL_SRC_FILES := hellocpp/main.cpp \
             hellocpp/test.cpp \    <--为新添加
可参考 [cocos2dx]Android.mk学习

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...