- 在 C++ 代码中声明和调用 Java 方法。
- 在 C++ 中获取 Java 的类、方法和字段。
- 在 Java 中编写被调用的方法。
- 在 CMakeLists.txt 中链接 JNI 库。
下面我们通过一个完整的、可运行的例子来详细讲解,这个例子将实现一个功能:点击 Cocos2d-x 场景中的一个按钮,弹出一个 Android 原生的 Toast 提示。

准备工作
- 确保你的 Cocos2d-x 项目已经正确配置并能运行在 Android 上,如果还没有,请使用 Cocos Creator 或 Cocos2d-x 命令行工具创建一个新项目,并成功构建到 Android 设备或模拟器上。
步骤 1: 在 Java 中编写被调用的方法
我们需要在 Java 代码中创建一个静态方法,这个方法将被 C++ 调用,这个方法的功能就是显示一个 Toast。
-
找到你的 Java 源文件,通常位于
proj.android/app/src/main/java/org/cocos2dx/cpp/AppActivity.java。cpp是默认的包名,如果你的项目不同,请相应调整。 -
修改
AppActivity.java,添加一个静态方法showToast。
// proj.android/app/src/main/java/org/cocos2dx/cpp/AppActivity.java
package org.cocos2dx.cpp; // 确保包名正确
import org.cocos2dx.lib.Cocos2dxActivity;
import android.os.Bundle;
import android.widget.Toast; // 导入 Toast 类
public class AppActivity extends Cocos2dxActivity {
// ... (原有的 onCreate 等方法保持不变)
/**
* 这是一个静态方法,可以被 C++ 通过 JNI 调用
* @param context 上下文,用于创建 Toast
* @param message 要显示的消息
*/
public static void showToast(String context, String message) {
// 在 UI 线程上显示 Toast
Toast.makeText(Cocos2dxActivity.getContext(), message, Toast.LENGTH_SHORT).show();
}
}
关键点:

public static: 方法必须是public和static的,这样 C++ 才能通过类名直接调用,而无需创建 Java 对象实例。Toast.makeText(...): 这是 Android 原生显示提示框的代码。Cocos2dxActivity.getContext(): Cocos2d-x 提供了一个静态方法来获取当前 Activity 的上下文,这是最安全的方式。
步骤 2: 在 C++ 中调用 Java 方法
我们将在 C++ 代码中编写逻辑来调用上面定义的 showToast 方法。
- 在 C++ 类的头文件或源文件中,包含必要的 JNI 头文件,并添加一个方法来触发调用。
假设我们在 HelloWorldScene.cpp 中实现这个功能。
// HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h" // 1. 包含 Cocos2d-x 的 JNI 辅助头文件
#include <jni.h> // 2. 标准 JNI 头文件
USING_NS_CC;
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
// 在 .cpp 文件顶部定义 Java 方法的签名
// 这个签名可以通过 javap 命令生成,我们手动写一个简单的
// (Ljava/lang/String;Ljava/lang/String;)V
// 解释: (L...;L...;)V -> 两个 String 参数,返回 void
static const char* className = "org/cocos2dx/cpp/AppActivity"; // 3. Java 类的全限定名 (包名/类名)
static const char* methodName = "showToast"; // 4. Java 方法名
static const char* methodSignature = "(Ljava/lang/String;Ljava/lang/String;)V"; // 5. Java 方法签名
// ... create() 和 init() 方法保持不变 ...
void HelloWorld::menuCloseCallback(Ref* pSender)
{
// 这里我们不调用 Director::getInstance()->end(),而是调用 Java 方法
callJavaToast();
}
void HelloWorld::callJavaToast()
{
// 6. 获取 JniMethodInfo
JniMethodInfo methodInfo;
// 7. 调用 JniHelper 的静态方法获取方法信息
// 参数: 方法信息对象, Java类名, Java方法名, 方法签名
if (JniHelper::getStaticMethodInfo(methodInfo,
className,
methodName,
methodSignature))
{
// 8. 调用 Java 方法
// 参数: jobject (this指针, 静态方法传NULL), jstring参数...
jstring jParam1 = methodInfo.env->NewStringUTF("Cocos2d-x"); // 第一个参数
jstring jParam2 = methodInfo.env->NewStringUTF("Hello from C++!"); // 第二个参数
methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jParam1, jParam2);
// 9. 释放局部引用
methodInfo.env->DeleteLocalRef(jParam1);
methodInfo.env->DeleteLocalRef(jParam2);
// 10. 释放方法信息
JniHelper::getEnv()->DeleteLocalRef(methodInfo.classID);
CCLOG("Successfully called Java showToast method.");
}
else
{
CCLOG("Failed to get static method info for showToast");
}
}
代码详解:
#include "platform/android/jni/JniHelper.h": Cocos2d-x 提供的 JNI 工具类,它封装了复杂的 JNI 调用,使代码更简洁。#include <jni.h>: 标准 JNI 接口定义。className: 非常重要!必须是 Java 类的全限定名,即包名/类名,用斜杠 而不是点 。methodName: 你要调用的 Java 静态方法名。methodSignature: 最复杂也最容易出错的部分,它描述了方法的参数和返回类型。- 开始, 结束。
- 中间是参数列表,每个参数对应一个类型码。
V表示void返回类型。- 常见类型码:
Z-> booleanB-> byteC-> charS-> shortI-> intJ-> longF-> floatD-> doubleLjava/lang/String;-> StringL.../...;-> 任意对象 (如Landroid/graphics/Bitmap;)
- 我们的
showToast方法有两个String参数,返回void,所以签名是(Ljava/lang/String;Ljava/lang/String;)V。
JniHelper::getStaticMethodInfo(...): 核心函数,用于查找并加载 Java 的静态方法,如果成功,它会填充JniMethodInfo结构体。methodInfo.env->NewStringUTF(...): C++ 字符串 (const char*) 不能直接传给 Java,需要用 JNI 环境指针env将其转换为jstring对象。methodInfo.env->CallStaticVoidMethod(...): 真正执行 Java 方法的地方。methodInfo.classID: Java 类的引用。methodInfo.methodID: Java 方法的引用。- 后面是转换后的参数。
DeleteLocalRef(...): 非常重要! 在 JNI 中,通过NewStringUTF等方法创建的局部引用,在使用完毕后必须手动释放,否则会导致内存泄漏。DeleteLocalRef(methodInfo.classID): 同样,getStaticMethodInfo返回的classID也是一个局部引用,需要释放。
步骤 3: 在 CMakeLists.txt 中链接 JNI 库
虽然 JniHelper 帮我们简化了很多,但底层的 JNI 调用仍然需要链接 Android 的 JNI 库。

打开 proj.android/CMakeLists.txt 文件,在 target_link_libraries 部分添加 log 和 android。
# ... 其他配置 ...
# Add your application library
add_library(${APP_NAME} SHARED
${COCOS2D_X_ANDROID_JNI_DIR}/cocos2dxjni.cpp 