luascriptcore
luascriptcore是一个用来绑定lua、java、oc的跨语言通信开源框架。被引入到了公司的项目中。
方法定义和绑定
lua call java
注册
使用的是带方法名的回调,即LuaContext#registerMethod(String, Callable)
。
- LuaContext持有了Java侧的Callable
- 通过LuaNativeUtil向LuaContext.cpp注册了该方法名和
LuaJavaEnv#luaMethodHandler
- 通过LuaSession(类似于ThreadLocal)获取到lua_state(原生lua)
- 依次把当前LuaContext、方法名、方法回调
LuaContext#methodRouteHandler
压栈 - 把栈顶元素(方法回调)设置为名为methodName的global对象并出栈
这里没有用lua_pushcfunction。
调用(LuaContext#methodRouteHandler
)
- 在虚拟栈中向上找到LuaContext和方法名
LuaContext#getMethodHandler
找到注册进来的LuaJavaEnv#luaMethodHandler
- 反解Lua数据至C++对象(详见参数序列化方式部分)
- 调用
LuaJavaEnv#luaMethodHandler
a. JNI反射查找到LuaContext.java#methodInvoke
b. 将3中反解的数据再封装成Java对象
c. 调用java方法,并获得返回值 - 释放对lua对象的引用
java call lua
注册
因为lua是纯动态化语言,不需要注册
调用
LuaContext.java#callMethod(String, LuaValue[]);
LuaNativeUtil#callMethod
->LuaContext::callMethod
- lua_getglobal找到对应的lua方法
- lua_pcall调用方法,并用context绑定的异常处理函数处理异常
- 处理栈顶的返回值(可能是多个或者一个)
- 序列化数据,并返回给java
类型约束及传输
分为primitive、collection和自定义三种。Primitive包括了Nil、Number、Boolean、String、Integer、Data(bytes)。Collection包括了Array、Map<String, Any>、Tuple。自定义包括了Object、Function和ptr。
JavaToLua
只有两大步
- 利用jni构建与LuaValue.java对应的LuaValue.cpp,LuaJavaConverter#convertToLuaValueByJLuaValue
- 直接把值push到lua stack中,LuaDataExchanger::pushStack
Primitive
- step1,jni调LuaValue#toXValue接口,传值获得具体的值
- step2,直接调lua_pushx接口,把具体值push。Data是当做string push进去的
Collection
- step1,把每个子元素读出来,按未知类型进行转换后填入到C++对应集合中
- step2,tuple比较特殊,直接挨个push进栈。另两个都被当做table来push。push的过程:
- 保证有全局的_G table(全局)和_G[‘vars’] table(变量),并将_G[‘vars’]置于栈顶
- 如果没有该对象(_G[‘vars’][table_id] == null)则新建一个包含所有元素的table,并置于栈顶
自定义
Object
- step1,能传递的Object必须是通过LuaJavaEnv#associcateInstance的对象,即以id->jobject形式存到了_instanceMap中。这里只是取出来二者,整合到一起
- step2,看有没有通过LuaExportsTypeManager注册过对应的类。
- 注册的,直接利用LuaExportsTypeManager使用反射+annotation的方式把序列化变成了metadata[’__index’]处理函数。利用LuaExportsTypeManager#globalIndexMetaMethodHandler来做数据读写
- 创建userData,并绑定到this
- 创建metadata,并metadata[’__gc’] = release方法,release方法内将this释放掉。
Function
- step1,只支持lua方法
- step2,function本来就是全局的变量,所以只是把对应的对象拿出来
Ptr
- step1,只支持lua对象,保存成了light-userdata
- step2,把light-userdata拿出来,push到栈顶
LuaToJava
三步:LuaDataExchanger#getValue把每个参数转成LuaValue。LuaJavaConverter#convertToJavaObjectByLuaValue把LuaValue.cpp转成JObject。LuaJavaConverter#convertToJavaLuaValueByLuaValue把JObject转成LuaValue.java。
Primitive
- step1,lua提供了各种toX的接口
- step2,搞成Box的对象
- step3,传值给jni,直接构造对应的LuaValue.java
Collection
- step1,都是按table读。key是Integer、从0开始、顺序递增就转成Array,其他转成Map
- step2,构建Java的List/Map,再反解每个元素,add或者put
- step3,传值给jni,直接构造对应的LuaValue.java
自定义
Object
- step1,获取userdata
- step2,只支持从java过去的对象,直接从userdata里拿到java对象
- step3,传值给jni,直接构造对应的LuaValue.java
Function
- step1,获取function,构建一个LuaFunction。通过让Global持有指针保证不释放,并利用引用计数来做释放(LuaDataExchanger#retainLuaObject)
- step2,把LuaFunction放到LuaObjectManager中
- step3,传值给jni,直接构造对应的LuaValue.java
Ptr
- step1,获取light-userdata,构建LuaPointer
- step2,构建LuaPointer
- step3,传值给jni,直接构造对应的LuaValue.java
其他细节
JNI对象的gc管理
java对象直接通过jni在native侧创建,并
- 将native侧的counterpart保存在LuaObjectManager中
- 将jni对象以GlobalWeakRef形式保存在LuaJavaEnv中,方便后续调用时快速查找