cocos2dx lua和java互调_runonglthread-程序员宅基地

技术标签: lua java cocos2dx  

原理介绍

第一步 lua调用java方法,会用到luaj,看源码

local luaj = {
    }
local callJavaStaticMethod = LuaJavaBridge.callStaticMethod
local function checkArguments(args, sig)
    if type(args) ~= "table" then args = {
    } end
    if sig then return args, sig end

    sig = {
    "("}
    for i, v in ipairs(args) do
        local t = type(v)
        if t == "number" then
            sig[#sig + 1] = "F"
        elseif t == "boolean" then
            sig[#sig + 1] = "Z"
        elseif t == "function" then
            sig[#sig + 1] = "I"
        else
            sig[#sig + 1] = "Ljava/lang/String;"
        end
    end
    sig[#sig + 1] = ")V"

    return args, table.concat(sig)
end

function luaj.callStaticMethod(className, methodName, args, sig)
    local args, sig = checkArguments(args, sig)
    --echoInfo("luaj.callStaticMethod(\"%s\",\n\t\"%s\",\n\targs,\n\t\"%s\"", className, methodName, sig)
    return callJavaStaticMethod(className, methodName, args, sig)
end

return luaj

在这份源码中看到了LuaJavaBridge.callStaticMethod,这个是一个全局变量,下面看一下它的实现
LuaJavaBridge他是一个c++的类被注册到了lua的全局表中
**

void LuaJavaBridge::luaopen_luaj(lua_State *L)
{
    
	s_luaState = L;
    lua_newtable(L);//生成一个table t放在栈顶L->top-1
    lua_pushstring(L, "callStaticMethod");//压入一个字符串funcName放在栈顶L->top-1
    lua_pushcfunction(L, LuaJavaBridge::callJavaStaticMethod);//压入一个c函数func放在栈顶L->top-1
    lua_rawset(L, -3);//设置t[funcName] = func,出栈两个元素,table t放在栈顶L->top-1
    lua_setglobal(L, "LuaJavaBridge");//设置全局变量G["LuaJavaBridge"] = t;
}

下面其实就进入到了C++调用java

/*
args:
    const char *className
    const char *methodName
    LUA_TABLE   args
    const char *sig
*/
int LuaJavaBridge::callJavaStaticMethod(lua_State *L)
{
    
    if (!lua_isstring(L, -4) || !lua_isstring(L, -3)  || !lua_istable(L, -2) || !lua_isstring(L, -1))
    {
    
    	lua_pushboolean(L, 0);
    	lua_pushinteger(L, LUAJ_ERR_INVALID_SIGNATURES);
    	return 2;
    }

    LOGD("%s", "LuaJavaBridge::callJavaStaticMethod(lua_State *L)");

    const char *className  = lua_tostring(L, -4);
    const char *methodName = lua_tostring(L, -3);
    const char *methodSig  = lua_tostring(L, -1);

    CallInfo call(className, methodName, methodSig);

    // check args
    lua_pop(L, 1);													/* L: args */
    int count = fetchArrayElements(L, -1);                      	/* L: args e1 e2 e3 e4 ... */
    jvalue *args = NULL;
    if (count > 0)
    {
    
	    args = new jvalue[count];
	    for (int i = 0; i < count; ++i)
	    {
    
	        int index = -count + i;
	        switch (call.argumentTypeAtIndex(i))
	        {
    
	            case TypeInteger:
	            	if (lua_isfunction(L, index))
	            	{
    
	                    args[i].i = retainLuaFunction(L, index, NULL);
	            	}
	            	else
	            	{
    
	            		args[i].i = (int)lua_tonumber(L, index);
	            	}
	                break;

	            case TypeFloat:
	                args[i].f = lua_tonumber(L, index);
	                break;

	            case TypeBoolean:
	                args[i].z = lua_toboolean(L, index) != 0 ? JNI_TRUE : JNI_FALSE;
	                break;

	            case TypeString:
	            default:
	                args[i].l = call.getEnv()->NewStringUTF(lua_tostring(L, index));
	                break;
	        }
	    }
	    lua_pop(L, count);                               			/* L: args */
    }

    bool success = args ? call.executeWithArgs(args) : call.execute();
    if (args) delete []args;

    if (!success)
    {
    
    	LOGD("LuaJavaBridge::callJavaStaticMethod(\"%s\", \"%s\", args, \"%s\") EXECUTE FAILURE, ERROR CODE: %d",
    			className, methodName, methodSig, call.getErrorCode());

    	lua_pushboolean(L, 0);
    	lua_pushinteger(L, call.getErrorCode());
    	return 2;
    }

	LOGD("LuaJavaBridge::callJavaStaticMethod(\"%s\", \"%s\", args, \"%s\") SUCCESS",
			className, methodName, methodSig);

	lua_pushboolean(L, 1);
	return 1 + call.pushReturnValue(L);
}

第二步在java中调用lua
需要在java中声明函数

package org.cocos2dx.lib;
public class Cocos2dxLuaJavaBridge
{
    
    public static native int callLuaFunctionWithString(int luaFunctionId, String value);
    public static native int callLuaGlobalFunctionWithString(String luaFunctionName, String value);
    public static native int retainLuaFunction(int luaFunctionId);
    public static native int releaseLuaFunction(int luaFunctionId);
}

在C++中实现这些函数


#include "Java_org_cocos2dx_lib_Cocos2dxLuaJavaBridge.h"
#include <android/log.h>
#include "CCLuaJavaBridge.h"
#include "base/ccUTF8.h"

#define  LOG_TAG    "Cocos2dxLuaJavaBridge_java"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

extern "C" {
    

JNIEXPORT jint JNICALL Java_org_cocos2dx_lib_Cocos2dxLuaJavaBridge_callLuaFunctionWithString
  (JNIEnv *env, jclass cls, jint functionId, jstring value)
{
    
    std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(env, value);
    int ret = LuaJavaBridge::callLuaFunctionById(functionId, strValue.c_str());
    return ret;
}

JNIEXPORT jint JNICALL Java_org_cocos2dx_lib_Cocos2dxLuaJavaBridge_callLuaGlobalFunctionWithString
  (JNIEnv *env, jclass cls, jstring luaFunctionName, jstring value)
{
    
    std::string functionNameStr = cocos2d::StringUtils::getStringUTFCharsJNI(env, luaFunctionName);
    std::string valueStr = cocos2d::StringUtils::getStringUTFCharsJNI(env, value);
    
    int ret = LuaJavaBridge::callLuaGlobalFunction(functionNameStr.c_str(), valueStr.c_str());
    return ret;
}

JNIEXPORT jint JNICALL Java_org_cocos2dx_lib_Cocos2dxLuaJavaBridge_retainLuaFunction
  (JNIEnv *env, jclass cls, jint luaFunctionId)
{
    
    return LuaJavaBridge::retainLuaFunctionById(luaFunctionId);
}

JNIEXPORT jint JNICALL Java_org_cocos2dx_lib_Cocos2dxLuaJavaBridge_releaseLuaFunction
  (JNIEnv *env, jclass cls, jint luaFunctionId)
{
    
    return LuaJavaBridge::releaseLuaFunctionById(luaFunctionId);
}

} // extern "C"

在看一下这个LuaJavaBridge::callLuaFunctionById(functionId, strValue.c_str())函数做了啥
前面说到LuaJavaBridge会被注册到lua的全局表中,嘿!没错,就在那个时候,这个对象偷偷记下了lua_State
这个lua_State在乱中就是灵魂的存在,有了它就可以操作lua了啊,下面的实现其实就是简单根据functionid去找lua的函数,找到以后就调用呗

int LuaJavaBridge::callLuaFunctionById(int functionId, const char *arg)
{
    
    lua_State *L = s_luaState;
    int top = lua_gettop(L);
                                                                /* L: */
    lua_pushstring(L, LUAJ_REGISTRY_FUNCTION);                  /* L: key */
    lua_rawget(L, LUA_REGISTRYINDEX);                           /* L: f_id */
    if (!lua_istable(L, -1))
    {
    
        lua_pop(L, 1);
        return -1;
    }

    lua_pushnil(L);                                             /* L: f_id nil */
    while (lua_next(L, -2) != 0)                                /* L: f_id f id */
    {
    
        int value = lua_tonumber(L, -1);
        lua_pop(L, 1);                                          /* L: f_id f */
        if (value == functionId)
        {
    
            lua_pushstring(L, arg);                             /* L: f_id f arg */
            int ok = lua_pcall(L, 1, 1, 0);                     /* L: f_id ret|err */
            int ret;
            if (ok == 0)
            {
    
                ret = lua_tonumber(L, -1);
            }
            else
            {
    
                ret = -ok;
            }

            lua_settop(L, top);
            return ret;
        }
    }                                                           /* L: f_id */

    lua_settop(L, top);
    return -1;
}

总结:
java和lua有相似之处,比如他们都是跑在虚拟机上,他们的虚拟机都是C写的
那么他们最终都是会编译成字节码,虚拟机在执行他们的时候,又会把他们通过语法分析和词法分析生成一条条机器指令
当lua程序运行起来的时候,可以把它理解为是lua虚拟机在执行机器指令,我们把c函数注册到lua的全局表中,当在lua中调用这个全局函数时,lua虚拟机就会去执行它的c函数
当java程序运行起来的时候,可以把它理解为是java虚拟机在执行指令,对于那些申明为native的java方法,它的实现在c++中,java虚拟机会把他编译为一条特殊的机器指令,当执行到这个指令的时候,就去调用c++函数了

我们用lua来调用java的方法,这个java方法是静态的,其实本质上还是用c来调用java,我们在lua中调用java的方法是c事先注册到lua中的全局变量c调用java,其实是获取java虚拟机,然后调用它的静态方法

我们用java来调用lua,其实是事先把lua函数注册到栈中的注册表里得到一个functionId,java会使用native方法来调用lua,这个方法,java虚拟机在执行它的时候,会去执行这个方法在其他语言的实现,这里的实现是在c语言端,所以c实现的这个方法并且带着functionId又去lua的栈中找之前注册函数lua的函数,找到以后再执行它

举个简单例子

lua端:
在一个可以被调用的地方,声明下面这个函数,比如按钮的回调

function MainScene:callJavaLogin()

    
    local callbackLua = function ( ... )
        print("与java端连接成功");
        self:readyLogin();
    end

    local className = "org/cocos2dx/lua/AppActivity"
    --java端对应的静态函数名
    local javaMethodName = "test"
    local args = {
     "hello android", callbackLua }
    local sigs = "(Ljava/lang/String;I)V" --传入string参数,无返回值  
    local luaj = require "cocos.cocos2d.luaj"
    local ok = luaj.callStaticMethod(className,javaMethodName,args,sigs)
    if not ok then
        print("call callback error")
    else
        print("call Back success")
    end
end

java端
在启动类AppActivity中实现下面这个静态方法

 //测试--------------------------
    public static void test(final String param,final int luaFunc) {
    
        System.out.println("----传过来的参数----param:"+param);
        System.out.println("-------luaFunc:"+luaFunc);
        
        /**
         * 给lua返回一个字符串
         */
        Cocos2dxLuaJavaBridge.callLuaFunctionWithString(luaFunc, "success");
        /**
         * 移除luaId
         */
        Cocos2dxLuaJavaBridge.releaseLuaFunction(luaFunc);
    }

打包即可观看结果

runOnGLThread与runOnUiThread

android的主线程指的就是UI线程,主要用来刷新界面和响应用户交互

在 cocos2d-x 启动后,Lua/C++代码将由 GL 线程调用,因此从 Lua/C++中调用的 Java 方法如果涉及到系统
用户界面的显示、更新操作,那么就必须让这部分代码切换到 UI 线程上去运行。
反之亦然,从 Java 调用 Lua/C++代码时,需要让这个调用在 GL 线程上执行,否则 Lua/C++代码虽然执行了,
但会无法更新 cocos2d-x 内部状态。
   确保 Lua/C++function 跑在 GL 线程(子线程),Java 代码跑在 UI 线程(主线程)

比如说支付,我们在游戏界面点击支付按钮,就是lua去调用java,那最后生成的支付界面一定要跑在UI线程里,同理如果我们想从java这边去调用某个游戏界面,那就需要在GL线程里操作
如果在android端有一个非常耗时的操作,才可以刷新界面,可以将这个耗时的操作写在线程里,然后将结果调用runOnUiThread来刷新界面UI

感悟

我们用lua语言写的代码,最后都要变成字节码,而在生成字节码的同时,操作码指令也都生成完毕,最后在虚拟机的大循环中去执行这些指令,lua的声明语句也好,lua的赋值语句也好,还是lua的函数等,其实这些都没有真正的执行,他们都会变成一条条相互关联的操作码指令,这些执行的操作码指令的偏移地址都是1,在我们注册到lua虚拟机的c或者c++函数的实现中,可以看到栈的操作,这就是操作码指令偏移地址为1造成的,我们写的lua语句所生成的操作码指令,其实就是一个入栈的过程,到最后执行c函数,就可以模仿栈的操作了,比如函数的调用,假设我们调用一个输出函数print,然后给这个函数传几个想要打印的字符串,形如print(a,b,c,d)
第一步:操作码指令:getglobal 全局函数(print)---------》0x00000001
第二步:操作码指令:loadk 加载字符串 a----------------》0x00000002
第三步:操作码指令:loadk 加载字符串 b----------------》0x00000003
第四步:操作码指令:loadk 加载字符串 c----------------》0x00000004
第五步:操作码指令:loadk 加载字符串 d----------------》0x00000005
第六步:操作码指令:callfunc 执行函数---------------------》 0x00000006
第七步:操作码指令:return 函数返回----------------------》 0x00000006
OK 可以进去print这个c函数中去看看,真的有栈的进出的操作,一句总结就是,我们写的lua语句,lua虚拟机根据这些语句为我们生成一条条偏移地址为1的操作码指令,这其实就符合了栈的操作,你可以理解其实我们写的代码就是入栈的操作,c函数在执行的时候就是读栈的操作,确切的说,是读寄存器的操作

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HzjCsdn/article/details/107918398

智能推荐

hive使用适用场景_大数据入门:Hive应用场景-程序员宅基地

文章浏览阅读5.8k次。在大数据的发展当中,大数据技术生态的组件,也在不断地拓展开来,而其中的Hive组件,作为Hadoop的数据仓库工具,可以实现对Hadoop集群当中的大规模数据进行相应的数据处理。今天我们的大数据入门分享,就主要来讲讲,Hive应用场景。关于Hive,首先需要明确的一点就是,Hive并非数据库,Hive所提供的数据存储、查询和分析功能,本质上来说,并非传统数据库所提供的存储、查询、分析功能。Hive..._hive应用场景

zblog采集-织梦全自动采集插件-织梦免费采集插件_zblog 网页采集插件-程序员宅基地

文章浏览阅读496次。Zblog是由Zblog开发团队开发的一款小巧而强大的基于Asp和PHP平台的开源程序,但是插件市场上的Zblog采集插件,没有一款能打的,要么就是没有SEO文章内容处理,要么就是功能单一。很少有适合SEO站长的Zblog采集。人们都知道Zblog采集接口都是对Zblog采集不熟悉的人做的,很多人采取模拟登陆的方法进行发布文章,也有很多人直接操作数据库发布文章,然而这些都或多或少的产生各种问题,发布速度慢、文章内容未经严格过滤,导致安全性问题、不能发Tag、不能自动创建分类等。但是使用Zblog采._zblog 网页采集插件

Flink学习四:提交Flink运行job_flink定时运行job-程序员宅基地

文章浏览阅读2.4k次,点赞2次,收藏2次。restUI页面提交1.1 添加上传jar包1.2 提交任务job1.3 查看提交的任务2. 命令行提交./flink-1.9.3/bin/flink run -c com.qu.wc.StreamWordCount -p 2 FlinkTutorial-1.0-SNAPSHOT.jar3. 命令行查看正在运行的job./flink-1.9.3/bin/flink list4. 命令行查看所有job./flink-1.9.3/bin/flink list --all._flink定时运行job

STM32-LED闪烁项目总结_嵌入式stm32闪烁led实验总结-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏6次。这个项目是基于STM32的LED闪烁项目,主要目的是让学习者熟悉STM32的基本操作和编程方法。在这个项目中,我们将使用STM32作为控制器,通过对GPIO口的控制实现LED灯的闪烁。这个STM32 LED闪烁的项目是一个非常简单的入门项目,但它可以帮助学习者熟悉STM32的编程方法和GPIO口的使用。在这个项目中,我们通过对GPIO口的控制实现了LED灯的闪烁。LED闪烁是STM32入门课程的基础操作之一,它旨在教学生如何使用STM32开发板控制LED灯的闪烁。_嵌入式stm32闪烁led实验总结

Debezium安装部署和将服务托管到systemctl-程序员宅基地

文章浏览阅读63次。本文介绍了安装和部署Debezium的详细步骤,并演示了如何将Debezium服务托管到systemctl以进行方便的管理。本文将详细介绍如何安装和部署Debezium,并将其服务托管到systemctl。解压缩后,将得到一个名为"debezium"的目录,其中包含Debezium的二进制文件和其他必要的资源。注意替换"ExecStart"中的"/path/to/debezium"为实际的Debezium目录路径。接下来,需要下载Debezium的压缩包,并将其解压到所需的目录。

Android 控制屏幕唤醒常亮或熄灭_android实现拿起手机亮屏-程序员宅基地

文章浏览阅读4.4k次。需求:在诗词曲文项目中,诗词整篇朗读的时候,文章没有读完会因为屏幕熄灭停止朗读。要求:在文章没有朗读完毕之前屏幕常亮,读完以后屏幕常亮关闭;1.权限配置:设置电源管理的权限。

随便推点

目标检测简介-程序员宅基地

文章浏览阅读2.3k次。目标检测简介、评估标准、经典算法_目标检测

记SQL server安装后无法连接127.0.0.1解决方法_sqlserver 127 0 01 无法连接-程序员宅基地

文章浏览阅读6.3k次,点赞4次,收藏9次。实训时需要安装SQL server2008 R所以我上网上找了一个.exe 的安装包链接:https://pan.baidu.com/s/1_FkhB8XJy3Js_rFADhdtmA提取码:ztki注:解压后1.04G安装时Microsoft需下载.NET,更新安装后会自动安装如下:点击第一个傻瓜式安装,唯一注意的是在修改路径的时候如下不可修改:到安装实例的时候就可以修改啦数据..._sqlserver 127 0 01 无法连接

js 获取对象的所有key值,用来遍历_js 遍历对象的key-程序员宅基地

文章浏览阅读7.4k次。1. Object.keys(item); 获取到了key之后就可以遍历的时候直接使用这个进行遍历所有的key跟valuevar infoItem={ name:'xiaowu', age:'18',}//的出来的keys就是[name,age]var keys=Object.keys(infoItem);2. 通常用于以下实力中 <div *ngFor="let item of keys"> <div>{{item}}.._js 遍历对象的key

粒子群算法(PSO)求解路径规划_粒子群算法路径规划-程序员宅基地

文章浏览阅读2.2w次,点赞51次,收藏310次。粒子群算法求解路径规划路径规划问题描述    给定环境信息,如果该环境内有障碍物,寻求起始点到目标点的最短路径, 并且路径不能与障碍物相交,如图 1.1.1 所示。1.2 粒子群算法求解1.2.1 求解思路    粒子群优化算法(PSO),粒子群中的每一个粒子都代表一个问题的可能解, 通过粒子个体的简单行为,群体内的信息交互实现问题求解的智能性。    在路径规划中,我们将每一条路径规划为一个粒子,每个粒子群群有 n 个粒 子,即有 n 条路径,同时,每个粒子又有 m 个染色体,即中间过渡点的_粒子群算法路径规划

量化评价:稳健的业绩评价指标_rar 海龟-程序员宅基地

文章浏览阅读353次。所谓稳健的评估指标,是指在评估的过程中数据的轻微变化并不会显著的影响一个统计指标。而不稳健的评估指标则相反,在对交易系统进行回测时,参数值的轻微变化会带来不稳健指标的大幅变化。对于不稳健的评估指标,任何对数据有影响的因素都会对测试结果产生过大的影响,这很容易导致数据过拟合。_rar 海龟

IAP在ARM Cortex-M3微控制器实现原理_value line devices connectivity line devices-程序员宅基地

文章浏览阅读607次,点赞2次,收藏7次。–基于STM32F103ZET6的UART通讯实现一、什么是IAP,为什么要IAPIAP即为In Application Programming(在应用中编程),一般情况下,以STM32F10x系列芯片为主控制器的设备在出厂时就已经使用J-Link仿真器将应用代码烧录了,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再使用J-Link重新烧录代码,这就增加了很多不必要的麻烦。站在用户的角度来说,就是能让用户自己来更换设备里边的代码程序而厂家这边只需要提供给_value line devices connectivity line devices