c++和java交互之jni

为了了解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,否则运行的时候会报错,不支持对象类型

原文地址:https://www.cnblogs.com/Colored-Mr/p/4721269.html