在一些业务场景中,我们可能会遇到需要在lua中调用Java代码的情况。当然,这可以用JNI来完成,但还有更方便的方法:LuaJavaBridge(LuaJava)和LuaJ。luaj的主要特点是可以从Lua中调用Java类静态方法。调用Java方法时,支持五种参数类型:int/float/boolean/String/Luafunction。可以将Lua函数作为参数传递给Java,让Java保存Lua函数的引用。从Java调用Lua的全局函数,或者调用引用指向的Lua函数luaj,都非常简单,但是已经完全满足集成各种SDK的需求。Luaj使用示例Java方法原型:publicstaticfloatgetNum(floatn){returnn;}lua调用示例:--Java类名localclassName="com/xttblog/Test"--调用本地方法的Java方法名='getNum'--调用Java方法所需要的参数localn=10localargs={n}--调用Java方法local_,testStaticMethod=luaj.callStaticMethod(className,method,args)Luaj实现原理有两个核心luaj的目标:从Lua调用Java,从Java调用Lua。梳理如下几点:查找并调用指定的Java方法查看调用结果,并获取Java方法的返回值将Lua函数作为参数传递给Java方法在Java方法中调用Lua函数查找并调用指定的Java方法JNI提供了FindClass()方法用于查找指定的Class,所以luaj.callStaticMethod()的第一个参数是要调用的JavaClass的完整类名(类名中的“.”应该是替换为“/”)。找到指定的Class后,使用JNI的GetStaticMethodID()方法找到该类的指定静态方法,前提是提供静态方法的名称和签名。所谓签名是指Java方法的参数类型和返回类型定义。方法的签名是一串像(Ljava/lang/String;ZZI)V这样的描述,可以通过字节码查看,如下例所示:这里想说的是luaj可以自动猜根据调用参数的方法签名。所以在例子中我们没有写签名。例子中指定了参数:localargs={n}luaj根据这个参数,会构造出正确的方法签名。注意:这里想说的是,Lua中没有办法准确判断一个值是整数还是浮点数,所以luaj在猜测方法签名的时候,假设所有的值都是浮点数点数。所以下面的调用会报错:publicstaticintgetNum(intn){returnn;}--Java类的名称localclassName="com/xttblog/Test"--调用local的Java方法名method='getNum'--调用Java方法需要的参数localn=10localargs={n}--调用Java方法local_,testStaticMethod=luaj.callStaticMethod(className,method,args)这个不行,所以这时候我们就得自己定义签名了。下面给出正确的例子:publicstaticintgetNum(intn){returnn;}--Java类名localclassName="com/xttblog/Test"--调用本地方法的Java方法名='getNum'--调用Java方法所需的参数localn=10localargs={n}--定义签名--参数:[I]nteger--返回值:[I]ntlocalsig="(I)I”--调用Java方法local_,testStaticMethod=luaj.callStaticMethod(className,method,args,sig)签名使用“(排列的参数类型)返回值类型”的格式,几个例子如下:说明()V参数:None,返回值:None(I)V参数:int,返回值:None(Ljava/lang/String;)Z参数:String,返回值:Boolean(IF)Ljava/lang/String;参数:Integer,Float,返回值:String这里列出了不同类型对应的Java签名。String:TypeNameTypeIInteger,orLuafunctionFFloatingpointnumberZBooleanvalueLjava/lang/String;StringVVoidVoid,仅用于指定Java方法不返回任何值Java方法中接收Lua函数的参数必须定义为int类型。从Java方法获取返回值luaj检查调用结果并从Java方法获取返回值。luaj在调用Java方法时,可能会出现各种错误,所以luaj提供了一种机制让Lua调用代码判断Java方法是否调用成功。luaj.callStaticMethod()返回两个值:成功时,第一个值为true,第二个值为Java方法的返回值(如果有)。失败时,第一个值为false,第二个值为错误代码。以下代码显示了如何检查返回结果并获取返回值:publicstaticintAddTwoNumbers(finalintnumber1,finalintnumber2){returnnumber1+number2;}Lua代码:localargs={2,3}localsig="(II)I"localok,ret=luaj.callStaticMethod(className,"AddTwoNumbers",args,sig)ifnotokthenprint("luajerror:",ret)elseprint("ret:",ret)--输出ret:5end错误码定义如下:错误码描述-1不支持的参数类型或返回值类型-2无效签名-3未找到指定的方法-4Java方法在执行过程中抛出异常-5Java虚拟机错误-6Java虚拟机中的错误将Lua函数作为参数传递给Java方法在Lua虚拟机中,Lua函数以值的形式保存。但是这个值不能被Java直接使用,所以luaj做了一个Lua函数引用表。当一个Lua函数被传递给Java时,这个函数对应的值会存储在引用表中,并得到一个唯一的引用ID(整数)。Java代码得到引用ID后,就可以轻松调用Lua函数了。所以Java方法中接收Lua函数的参数必须定义为int类型。例:publicstaticintgetNum(intn){returnn;}localfunctioncallback(result)---方法内容end--Java类名localclassName="com/xttblog/Test"--方法名Java方法调用localmethod='getNum'--调用Java方法所需的参数localargs={callback}--定义签名--参数:[I]nteger--返回值:[I]ntlocalsig="(I)I"--CallJavamethodlocal_,testStaticMethod=luaj.callStaticMethod(className,method,args,sig)另外,LuaJ也很好用。只需导入pom.?然后直接将lua代码作为String字符串嵌入Java代码中:StringluaStr="print'hello,world!'";Globalsglobals=JsePlatform.standardGlobals();LuaValuechunk=globals.load(luaStr);chunk.call();也可以创建一个login.lua脚本,内容如下:--无参数函数functionhello()print'hello'end--带参数函数functiontest(str)print('datafromjavais:'..str)return'haha'end然后,Java首先加载login.lua脚本并编译,然后获取指定名称的函数。如果没有参数,直接使用call()方法,如果有参数,需要通过invoke(LuaValue[])传入参数列表:StringluaPath="res/lua/login.lua";//lua脚本文件所在路径globalsglobals=JsePlatform.standardGlobals();//加载脚本文件login.lua,编译globals.loadfile(luaPath).call();//获取无参函数helloLuaValuefunc=globals.get(LuaValue.valueOf("hello"));//执行hello方法func.call();//获取参数化函数testLuaValuefunc1=globals.get(LuaValue.valueOf("test"));//执行测试方法,传入String类型参数参数Stringdata=func1.call(LuaValue.valueOf("I'amfromJava!")).toString();//打印lua函数返回的数据Logger.info("lua返回的数据是:"+data);运行结果如下:hellodatafromjavais:I'mfromJava!八月07,20225:31:25pmcom.tw.login.tools.Loggerinfo信息:lua返回数据:哈哈
