为了了解jni,我们先来个简单的需求。在cocos2d-x 3.3的项目中添加一个退出的按钮。点击按钮调用java的方法,然后通过java方法再调用C++的方法实现游戏的退出(其实就是绕一大圈为了给大家说明一下jni)
接下来我们找一张退出按钮的图:
。就这一张了。命名为exit.png
接下来我们创建一个按钮Menu,并添加点击调用事件:
MenuItemImage * imgClose = MenuItemImage::create( "exit.png", "exit.png", CC_CALLBACK_0( HelloWorld::GameCloseCallBack, this ) ); Menu * menuCLose = Menu::create( imgClose, NULL ); menuCLose->setPosition( Vec2( 400, 200 ) ); sprite->addChild( menuCLose );
接下来我们写点击按钮后调用的方法HelloWorld::GameCloseCallBack,然后我们在方法里面调用CppInterface::GameCloseCallBack();
void HelloWorld::GameCloseCallBack() { CppInterface::GameCloseCallBack(); }
接下来我们去写CppInterface::GameCloseCallBack()方法(也就是CppInterface的静态方法GameCloseCallBack())
CppInterface.h
#ifndef __CPP_INTERFACE_H__ #define __CPP_INTERFACE_H__ #define AndroidClassName "org/game/lib/CppInterface" class CppInterface { public: // 退出游戏(调用java) static void GameCloseCallBack(); // 退出游戏(真正的退出) static void ExitGame(); private: }; #endif // __HELLOWORLD_SCENE_H__
CppInterface.cpp
#include "CppInterface.h"
#include <cocos2d.h> #if defined(ANDROID) #include <platform/android/jni/JniHelper.h> #endif using namespace cocos2d; /* ===================== CppInterface::GameCloseCallBack ===================== */ void CppInterface::GameCloseCallBack() {
// android平台
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; if( JniHelper::getStaticMethodInfo( methodInfo, AndroidClassName,"GameBack","()V") ) { methodInfo.env->CallStaticVoidMethod( methodInfo.classID, methodInfo.methodID ); } #else Director::getInstance()->end(); #endif } /* ==================== CppInterface::ExitGame ==================== */ void CppInterface::ExitGame() { Director::getInstance()->end(); }
我给大家讲解一下:CppInterface.h中,AndroidClassName里面放的是C++调用java方法所在的包名及类名(也就是告诉C++,你要调用的java方法在哪里),中间用"/"隔开。
对于CppInterface.cpp中GameCloseCallBack()方法中
JniHelper::getStaticMethodInfo( methodInfo, AndroidClassName,"GameBack","()V") )
GameBack为C++调用java中的方法名,“()V”:这个参数说的是C++调用java方法传参以及返回值,括号中填写的是C++调用java方法依次传的参数类型,无传参则不写。V表示void,表示方法无返回值
ExitGame(),这个方法预留的是java调用C++的接口。来做真正的游戏退出
接下来我们去做java的方法。
在android工程下创建类CppInterface.java,类所在包名org.game.lib,代码如下:
package org.game.lib; public class CppInterface { // 退出游戏(供C++调用) public static void GameBack() { JavaInterface.GameBack(); } }
我们在同级目录下创建JavaInterface.java,代码如下:
package org.game.lib; public class JavaInterface { // 调用C++退出游戏的方法 public static native void GameBack(); }
接下来我们在回到C++这边。在Classes目录中创建文件JavaInterface.cpp(不用创建头文件JavaInterface.h).代码如下:
#if defined(ANDROID) #include <platform/android/jni/JniHelper.h> #include "CppInterface.h" extern "C" { void Java_org_game_lib_JavaInterface_GameBack( JNIEnv* env, jobject thiz ){
// 退出游戏 CppInterface::ExitGame(); } } #endif
这里要特别注意静态方法多了个native,加了native的静态方法,才能进入C++中调用extern "C"{}中的方法,你们可以看C++的方法名。其实就是java调用C++的方法所在的包名,类名,方法名组合起来的(这里指的是java的包名。类名),中间用下划线隔开,格式必须这样,不然调用不到!!格式为Java_包名_类名_方法名
OK,接下来仔细阅读代码,基本上我们已经绕了一圈回来了。jni的C++和java互调就是这样
基本流程就是=>退出按钮调用CppInterface类的静态方法GameCloseCallBack()->调用org.game.lib包名下的CppInterface.java的静态方法GameBack()->调用JavaInterface.java的静态方法GameBack() ->调用JavaInterface.cpp中extern "C"{}里面的Java_org_game_lib_JavaInterface_GameBack方法->调用CppInterface的ExitGame()。
对于java传递过来的参数,boolean=》bool和int=》int外,string传过来就是jstring了,记得要转换,转换方法为:
比如有个方法
java方法:
// 设置版本号
public static native void SetBatchversion( final String jBatchversion );
C++:
void Java_org_game_lib_JavaInterface_SetBatchversion( JNIEnv* env, jobject thiz, jstring jBatchversion ){
// 转换参数
const char* chBatchversion = env->GetStringUTFChars( jBatchversion, NULL );
}
C++=》java传参的类型的对于表:
------------------------------
类型 符号
bool Z
byte B
char C
short S
int I
long L
float F
double D
void V
std::string Ljava/lang/String;
-------------------------------------
如果C++调用java要传参,则bool和int不需要转换,直接传。
void ypCppInterface::SetIsShowPayDialog( bool isShow ) {
// android平台 #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; if( JniHelper::getStaticMethodInfo(methodInfo, AndroidClassName, "ShowPayDialog", "(Z)V") ) { methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, isShow); } #endif }
但是如果是str:string或者const char*,则需要转换。代码如下:
void ypCppInterface::InitLabelText( std::string httpRet ) {
// android平台 #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; if( JniHelper::getStaticMethodInfo(methodInfo, AndroidClassName, "InitLabelText", "(Ljava/lang/String;)V") ) {
// 转换后传给java jstring jHttpRet = methodInfo.env->NewStringUTF( httpRet.c_str() ); methodInfo.env->CallStaticVoidMethod( methodInfo.classID, methodInfo.methodID, jHttpRet ); } #endif }
还有一种就是有返回值的。如:
std::string ypCppInterface::JSONToString( const char* jsonString, const char* strName ) { std::string strRet = "0"; #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; if( JniHelper::getStaticMethodInfo(methodInfo, AndroidClassName, "JSONToString", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") ) {
// 转换后传给java jstring jstrJsonString = methodInfo.env->NewStringUTF(jsonString); jstring jstrName = methodInfo.env->NewStringUTF(strName); // 调用java有返回值的方法,得到返回值 jstring jstrRet = (jstring)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, jstrJsonString, jstrName);
// 转换类型,方便C++使用 const char* chRet = methodInfo.env->GetStringUTFChars(jstrRet, NULL); strRet = chRet; } #endif return strRet; }
不过在java那边接受的参数是不需要转换的:
// json解析 public static String JSONToString( final String jsonString, final String strName ) { return MobileDataStatus.JsonToString( "code", "0", "data", jsonString, strName ); }
整个调用流程就是这样。。。 希望大家能看懂。。。 文笔不好。。。谅解!!!
最后给大家提醒一下,在这里,一定不能把int换成Integer,否则运行的时候会报错,不支持对象类型