Lua 嵌入 Android 原理

Lua 作为一个轻量、灵活的嵌入式脚本语言,可供任何需要的程序使用。lua 可利用 C API 方便的嵌入其他系统。

Lua 与 C 交互

通过虚拟栈 栈顶元素索引 -1,栈底元素索引 1

C 调用 Lua

a.lua

1
2
3
4
5
6
print("hello from lua")

function power(a,n)
return a^n
end

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lua_State * L = luaL_newstate();    // 创建 Lua 与 C 进行数据交互的堆栈并返回指针 L;
luaL_openlibs(L); //为堆栈加载所有 Lua 的标准库

// 1. 执行 lua 脚本
luaL_dostring(L,"print('from dostring')");

// 2. 执行整个 Lua 脚本文件 a.lua
luaL_dofile(L,"a.lua");

// 3. 调用 Lua 中的方法
lua_getglobal(L,"power");
lua_pushinteger(L,2);
lua_pushinteger(L,10);
lua_call(L,2,1);
int res = lua_tonumber(L,-1); // 取出栈顶结果
printf("power:%d",res);

lua_close(L); //函数关闭堆栈,释放资源

Lua 调用 C

b.lua

1
2
local res = add(20,3)
print('res:'..res)

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//待Lua调用的C注册函数
int add(lua_State* L)
{
double op1 = lua_tonumber(L,-1);
double op2 = lua_tonumber(L,-2);

//将函数的结果压入栈中。如果有多个返回值,可以在这里多次压入栈中。
lua_pushnumber(L,op1 + op2);

return 1; //返回值表示返回值数量,即压入栈中的返回值数量。
}

void lua_call_c()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_register(L,"add",add);
luaL_dofile(L,"b.lua");
lua_close(L);
}

int main()
{
lua_call_c();
return 0;
}

Java 与 C 交互

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C/C++)。

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.jni_java;

public class Main {

static native int add(int a, int b);

static {
System.loadLibrary("libjni_c");
}

public static void main(String[] args) {
System.out.println("hello");
System.out.println("add form c:" + add(2, 3));
}

}

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
* main.c
* Author: hanks
*/
#include <stdio.h>
#include "com_jni_java_Main.h"

JNIEXPORT jint JNICALL Java_com_jni_1java_Main_add(JNIEnv * env, jclass job,
jint n1, jint n2) {

// jni diaoyong java
jclass clazz = (*env)->FindClass(env, "com/jni_java/BigOp");
if(clazz == NULL){
printf("找不到类");
}

jmethodID mid_static_method = (*env)->GetStaticMethodID(env, clazz, "bigNumberAdd", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
if (mid_static_method == NULL) {
printf("找不到函数");
}

jstring a = (*env)->NewStringUTF(env, "111111111111111111111111111");
jstring b = (*env)->NewStringUTF(env, "22222222222222222222222222");
jobject res = (*env)->CallStaticObjectMethod(env, clazz, mid_static_method, a, b);
const char *resultChar = (*env)->GetStringUTFChars(env, res, NULL);
printf("bigAdd: %s", resultChar);

// 删除引用
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, a);
(*env)->DeleteLocalRef(env, b);

return n1 + n2;
}

Lua 与 Java 交互

实际上也就是 lua <-> c <-> java
可以将 lua 编译成对应平台的库 so/dll ,然后嵌入 Java 项目。
介绍一下开源项目 luajava ,该项目实现以下几个方法 借助这几个方法 lua 几乎可以使用所有的 java 类了,参考 luajava.c

  1. newInstance(className, ……)
    第一个参数是类名,后面的是构造方法参数
1
local obj = luajava.newInstance("java.lang.Object")
  1. bindClass(className)
    这个方法是用来调用类的静态方法的,返回一个java class
1
2
local sys = luajava.bindClass("java.lang.System")
print ( sys:currentTimeMillis() )
  1. new(javaClass)
    创建一个实例
1
2
local str = luajava.bindClass("java.lang.String")
strInstance = luajava.new(str)
  1. createProxy(interfaceNames, luaObject)
    这个是使用 java 接口的方法。luaObject 中实现 java 接口中的方法,然后调用 creatProxy 来构建出这个接口的 lua 版本,相当于用 luaObject来实现 java 接口了。

可以多个接口同时调用,然后luaObject实现所有这些接口的方法

1
2
3
4
5
6
7
local button = luajava.newInstance("java.awt.Button", "execute")
local buttonProxy = luajava.createProxy("java.awt.ActionListener", {
actionPerformed = function(ev)
-- body
end
})
button:addActionListener(buttonProxy)
  1. loadLib(className, methodName)
    调研类的静态方法

Lua 与 Android 交互

通过 NDK 编程,编译对应 Android 平台的 so 库,然后调研 Android SDK 来开发 Android

androlua,改进版本 AndroLua_pro
主要可参考其中 的 import.lualoadlayout.lua,这两个 lua 文件将如何调用 java 中的类和方法进行优化,使得调用起来非常方便。

相关代码实现

lua_into_android

文章来自: https://hanks.pub