杰瑞科技汇

Cocos Lua如何调用Java方法?

Lua 发送一个带有特定参数的请求给 Java,Java 在一个独立的线程中执行这个请求,执行完毕后(可选)再将结果返回给 Lua。

Cocos Lua如何调用Java方法?-图1
(图片来源网络,侵删)

下面我将分步详细讲解如何实现,并提供一个完整的示例。


核心概念:JsbBridge

JsbBridge 提供了两个主要的方法:

  1. callStaticMethod: 这是最常用的方法,用于调用 Java 的静态方法
  2. reflectionCall: 用于通过反射调用 Java 的实例方法或构造函数,相对复杂,一般较少使用。

我们主要讲解 callStaticMethod


第一步:在 Cocos Creator (Lua) 中调用 Java

在 Lua 脚本中,使用 JsbBridge.callStaticMethod 来发起调用。

Cocos Lua如何调用Java方法?-图2
(图片来源网络,侵删)

callStaticMethod 的方法签名

JsbBridge.callStaticMethod(className, methodName, paramType, parameters, callback)
  • className (string): Java 类的全限定名,格式为 包名.类名
  • methodName (string): 要调用的 Java 静态方法名
  • paramType (string or table): 参数类型描述,如果只有一个参数,可以是字符串(如 "I" 代表 int);如果有多个参数,则需要是一个类型字符串数组(如 {"I", "Ljava/lang/String;"})。
  • parameters (any or table): 要传递给 Java 方法的参数,如果只有一个参数,可以直接传入;如果有多个参数,需要按顺序放入一个表中。
  • callback (function, 可选): 回调函数,当 Java 方法执行完毕后,如果需要返回结果给 Lua,会通过这个回调函数返回,这个回调在主线程中执行。

Java 参数类型与 Lua 的对应关系

Java 类型 JNI 类型描述符 Lua 类型 备注
int, Integer I number
float, Float F number
double, Double D number
boolean, Boolean Z boolean Lua 中用 true/false
String Ljava/lang/String; string
byte, Byte B number
char, Character C string 传入单个字符的字符串
int[] [I table {1, 2, 3}
String[] [Ljava/lang/String; table {"a", "b", "c"}

第二步:在 Android (Java) 中接收调用

在 Java 端,你需要创建一个类,并使用 @Jni@Jsi 注解来告诉引擎哪些方法可以被 Lua 调用,这些注解通常位于 cocos-service 库中。

添加依赖

确保你的 app/build.gradle 文件中包含了 cocos-service 依赖,对于较新版本的 Cocos Creator,这通常是自动配置的。

// app/build.gradle
dependencies {
    // ... 其他依赖
    implementation project(':cocos-service') // 确保这行存在
}

创建 Java 类并添加注解

创建一个 Java 类,com.yourpackage.MyJavaBridge

package com.yourpackage;
import com.cocos.service.bridge.Jni;
import com.cocos.service.bridge.Jsi;
public class MyJavaBridge {
    // 被 @Jsi 注解标记的静态方法可以被 Lua 调用
    // 参数和返回值类型必须与 JNI 兼容
    @Jsi
    public static String showToast(String message, int duration) {
        // 注意:这个方法运行在**后台线程**,不能直接操作 UI!
        // 如果要更新 UI,需要使用 Handler 切换到主线程。
        Log.d("MyJavaBridge", "Received message from Lua: " + message + ", Duration: " + duration);
        // 使用 Handler 切换到主线程显示 Toast
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(CocosHelper.getActivity(), message, duration).show();
            }
        });
        // 返回一个字符串给 Lua
        return "Message '" + message + "' is being shown.";
    }
    @Jsi
    public static int addNumbers(int a, int b) {
        Log.d("MyJavaBridge", "Adding " + a + " and " + b);
        return a + b;
    }
}

代码解释

  • package com.yourpackage;: 包名必须与你在 Lua 中传入的 className 中的包名一致。
  • @Jsi: 这个注解是关键,它告诉 Cocos Service 引擎,这个静态方法可以被 Lua 调用。
  • 线程问题:非常重要!@Jsi 标记的方法运行在后台线程(非 UI 线程),任何 UI 操作(如显示 Toast、更新 View)都必须通过 Handler 切换到主线程执行,上面的 showToast 示例就展示了正确的做法。
  • CocosHelper.getActivity(): 这是一个 Cocos Creator 提供的工具类,可以方便地获取到当前 Activity 的实例。

第三步:完整示例

场景描述

在 Cocos Creator 场景中,有一个按钮,点击按钮后,Lua 会调用 Java 的 showToast 方法,显示一个 Toast;然后调用 addNumbers 方法,并将结果打印到控制台。

Java 端代码

按照第二步,创建 MyJavaBridge.java 文件,内容如下:

// MyJavaBridge.java
package com.yourpackage;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.cocos.service.bridge.Jsi;
import com.cocos.service.CocosHelper;
public class MyJavaBridge {
    private static final String TAG = "MyJavaBridge";
    @Jsi
    public static String showToast(String message, int duration) {
        Log.d(TAG, "Java showToast called with: " + message);
        // 切换到主线程显示 Toast
        new Handler(Looper.getMainLooper()).post(() -> {
            Toast.makeText(CocosHelper.getActivity(), message, duration).show();
        });
        return "Java: Toast for '" + message + "' has been queued.";
    }
    @Jsi
    public static int addNumbers(int a, int b) {
        Log.d(TAG, "Java addNumbers called with: " + a + ", " + b);
        return a + b;
    }
}

Lua 端代码

在 Cocos Creator 中创建一个 Lua 脚本,MainScene.lua,并把它挂载到场景的节点上。

-- MainScene.lua
local MainScene = class("MainScene", function()
    return cc.Scene:create()
end)
function MainScene:ctor()
    -- 加载 CCB 文件或创建 UI 节点
    -- 假设我们有一个按钮,它的 name 是 "myButton"
    local node = cc.CSLoader:createNode("MainScene.csb")
    self:addChild(node)
    local button = node:getChildByName("myButton")
    button:addClickEventListener(function()
        self:onButtonClick()
    end)
end
function MainScene:onButtonClick()
    print("Button clicked! Calling Java...")
    -- --- 示例 1: 调用 showToast 方法 ---
    -- className: "com.yourpackage.MyJavaBridge"
    -- methodName: "showToast"
    -- paramType: 一个字符串数组,描述两个参数的类型
    --   "Ljava/lang/String;" -> String
    --   "I" -> int
    -- parameters: 参数表,顺序必须与 paramType 一致
    -- callback: 回调函数,用于接收 Java 返回的结果
    local callback1 = function(result)
        -- 这个回调在 Lua 的主线程中执行
        print("Lua: Callback from showToast received result:", result)
    end
    local className1 = "com.yourpackage.MyJavaBridge"
    local methodName1 = "showToast"
    local paramType1 = {"Ljava/lang/String;", "I"}
    local parameters1 = {"Hello from Lua!", Toast.LENGTH_LONG} -- Toast.LENGTH_LONG 是一个常量
    -- 发起调用
    JsbBridge.callStaticMethod(className1, methodName1, paramType1, parameters1, callback1)
    -- --- 示例 2: 调用 addNumbers 方法 ---
    local callback2 = function(result)
        print("Lua: Callback from addNumbers received result:", result, "(type: ", type(result), ")")
    end
    local className2 = "com.yourpackage.MyJavaBridge"
    local methodName2 = "addNumbers"
    local paramType2 = {"I", "I"}
    local parameters2 = {10, 20}
    JsbBridge.callStaticMethod(className2, methodName2, paramType2, parameters2, callback2)
end
return MainScene

注意事项

  • 包名一致性:Java 类的包名 com.yourpackage 必须与 Lua 中 className 的部分完全一致。
  • 方法名一致性:Lua 传入的 methodName 必须与 Java 方法名完全一致。
  • 参数类型和顺序paramTypeparameters 的类型和顺序必须严格对应,否则会调用失败。
  • 异步性callStaticMethod 是异步的,Java 方法执行后,结果通过 callback 返回,不要期望调用后立即能拿到返回值。
  • 错误处理:如果调用失败(例如类名、方法名错误),callback 不会被调用,你可以通过引擎的日志来排查问题,在 Android Studio 的 Logcat 中,过滤标签 cocosJsbBridge 通常能看到相关的错误信息。
  • 混淆:在发布 Android 版本时,如果启用了代码混淆,必须保留被 @Jsi 注解标记的类和方法,否则它们会被混淆掉,导致 Lua 无法找到,在 proguard-rules.pro 文件中添加如下规则:
    # Keep Cocos Service JNI classes
    -keep class com.cocos.service.** { *; }
    # Keep your own bridge class and methods
    -keep class com.yourpackage.MyJavaBridge { *; }

调试技巧

  1. Logcat: 这是你最好的朋友,在 Android Studio 中,打开 Logcat,选择你的 App,然后使用过滤器 cocosJsbBridge,你会在日志中看到引擎发送的请求、参数以及可能的错误信息。
  2. Lua 打印: 在 Lua 回调函数中大量使用 print() 来打印收到的结果和中间状态,确认 Lua 端的逻辑是否正确。
  3. 逐步排查
    • 先确保 Java 类和方法名、注解都正确。
    • 再确保 Lua 传入的 classNamemethodName 拼写无误。
    • 然后检查 paramTypeparameters 的类型和顺序是否一一对应。
    • 最后检查 Java 方法内部的逻辑,特别是线程切换是否正确。

通过以上步骤,你就可以在 Cocos Creator 的 Lua 项目中成功调用 Java 代码了。

分享:
扫描分享到社交APP
上一篇
下一篇