--- url: /nexlua/LuaContext.md --- # LuaContext `LuaContext` 是 NexLua 提供的接口,用于管理和操作 Lua 环境。以下是该接口的主要 API: ## 获取类加载器与 Lua 实例 ```java ArrayList getClassLoaders(); Lua getLua(); ``` * `getClassLoaders()`:返回一个包含类加载器的列表。 * `getLua()`:获取 Lua 实例。 ## 获取 Lua 配置与路径 ```java LuaConfig getConfig(); String getLuaDir(); String getLuaPath(); String getLuaLpath(); String getLuaCpath(); ``` * `getConfig()`:获取 Lua 配置。 * `getLuaDir()`、`getLuaPath()`、`getLuaLpath()`、`getLuaCpath()`:分别获取 Lua 的目录和路径信息。 ## 上下文与消息处理 ```java Context getContext(); void showToast(String message); void sendMessage(String message); void sendError(Exception e); ``` * `getContext()`:获取上下文对象。 * `showToast(String message)`:显示提示信息。 * `sendMessage(String message)`:发送消息。 * `sendError(Exception e)`:处理异常错误。 ## 执行 Lua 函数 ```java default boolean runFunc(String funcName, Object... args) { return LuaContextUtils.runFunc(getLua(), funcName, args); } default Object runFunc(String funcName, Class clazz, Object... args) { return LuaContextUtils.runFunc(getLua(), funcName, clazz, args); } ``` * `runFunc(String funcName, Object... args)`:执行指定名称的 Lua 函数,返回布尔值结果。 * `runFunc(String funcName, Class clazz, Object... args)`:执行指定名称的 Lua 函数,并指定返回值类型。 # LuaApplication 在 LuaApplication 的环境中有以下全局变量,`application`, `app`, `context`, `this`,这些变量都指向 LuaApplication 对象。 LuaApplication 支持以下生命周期函数:`onCreate`, `onTerminate`, `onLowMemory`, `onTrimMemory` 和 `onConfigurationChanged`。 支持以下生命周期函数: `main` `onCreate()` `onTerminate()` `onLowMemory()` `onTrimMemory()` `onConfigurationChanged()` `sendMessage()` `sendError()` # LuaActivity 在 LuaActivity 的环境中有以下全局变量,`activity`, `this`, `context`,这些变量都指向 LuaActivity 对象。还有 `application`, `app` 指向 LuaApplication 对象。 支持以下生命周期函数: `main` `onCreate()` `onStart()` `onResume()` `onPause()` `onStop()` `onDestroy()` `onRestart()` `onSaveInstanceState()` `onRestoreInstanceState()` `onConfigurationChanged()` `onNewIntent()` `onRequestPermissionsResult()` `onActivityResult()` `onContentChanged()` `onCreateOptionsMenu()` `onOptionsItemSelected()` `onMenuItemSelected()` `onCreateContextMenu()` `onContextItemSelected()` `onKeyShortcut()` `onKeyDown()` `onKeyUp()` `onKeyLongPress()` `onTouchEvent()` `onReceive()` `sendMessage()` `sendError()` 你可以在 Lua 中创建个全局的函数,当上述生命周期函数触发时会调用 Lua 中的全局函数。 若有遗漏欢迎向仓库提交 Issues 或 Pull Request。 --- --- url: /nexlua/LuaUtil.md --- # LuaUtil `com.nexlua.LuaUtil` 提供了 Android 开发中常用的 IO、资源、压缩及哈希工具方法。 ::: tip 初始化 一般 `LuaApplication` 会在 `onCreate` 自动调用 `LuaUtil.init`,开发者通常无需手动调用。 ::: ## 核心环境 (Context & Init) ```java // 初始化工具类 public static void init(Context ctx) // 获取全局 Application Context public static Context getContext() // 获取全局 AssetManager public static AssetManager getAssetManager() ``` ## 缓冲区辅助 (Buffer) ```java // 将 byte[] 包装为 DirectByteBuffer (NIO) public static ByteBuffer wrap(byte[] bytes) ``` ## 流操作 (Stream Utils) 包含基础流操作和自动关闭流(AutoClose)的变体。 ### 基础流操作 (需手动关闭流) ```java // 读取流的所有字节 public static byte[] readStreamBytes(InputStream in) throws IOException // 读取流到指定大小的 ByteBuffer // 如果流的内容超过 knownSize,会抛出 IOException public static ByteBuffer readStreamBuffer(InputStream in, int knownSize) throws IOException // 将输入流复制到输出流 public static void copyStream(InputStream in, OutputStream out) throws IOException ``` ### 自动关闭流操作 以下方法在执行完毕或发生异常时会自动关闭传入的 `InputStream` 或 `OutputStream`。 ```java // 读取流并自动关闭 public static byte[] readStreamBytesWithAutoClose(InputStream in) throws IOException // 读取流到 ByteBuffer 并自动关闭 public static ByteBuffer readStreamBufferWithAutoClose(InputStream in, int knownSize) throws IOException // 复制流并自动关闭输入和输出流 public static void copyStreamWithAutoClose(InputStream in, OutputStream out) throws IOException ``` ## 文件操作 (File Utils) ```java // 读取文件到 ByteBuffer public static ByteBuffer readFileBuffer(File file) throws IOException // 读取文件所有字节 public static byte[] readFileBytes(File file) throws IOException // 读取文件为字符串 (UTF-8) public static String readFile(File file) throws IOException // 删除文件 public static boolean rmFile(File file) // 递归删除文件夹及其内容 public static boolean rmDir(File file) // 复制文件 public static void copyFile(File src, File dest) throws IOException // 递归复制文件夹 public static void copyDir(File srcDir, File destDir) throws IOException ``` ## Assets 资源操作 ```java // 列出 assets 目录下的文件 public static String[] listAssets(String assetPath) throws IOException // 判断 assets 文件是否存在 public static boolean isAssetExists(String assetPath) // 读取 assets 文件到 ByteBuffer (尝试使用 mmap,失败则降级为流读取) public static ByteBuffer readAssetBuffer(String assetPath) throws IOException // 读取 assets 文件所有字节 public static byte[] readAssetBytes(String assetPath) throws IOException // 读取 assets 文件为字符串 (UTF-8) public static String readAsset(String assetPath) throws IOException // 将 assets 文件复制到本地文件 public static void copyAssetsFile(String assetPath, File destFile) throws IOException // 递归复制 assets 目录到本地目录 public static void copyAssetsDir(String assetPath, File destDir) throws IOException ``` ## Raw 资源操作 (res/raw) ```java // 读取 Raw 资源到 ByteBuffer public static ByteBuffer readRawBuffer(int id) throws IOException // 读取 Raw 资源所有字节 public static byte[] readRawBytes(int id) throws IOException // 读取 Raw 资源为字符串 (UTF-8) public static String readRaw(int id) throws IOException // 将 Raw 资源复制到本地文件 public static void copyRawFile(int id, File destFile) throws IOException ``` ## 压缩与解压 (Zip Utils) ```java // 压缩文件或文件夹到指定 zip 文件 public static void zip(File srcFile, File zipFile) throws IOException // 递归压缩内部逻辑方法 (通常供 zip 调用,但也为 public) public static void zipInternal(ZipOutputStream zipOutputStream, File file, String baseName) throws IOException // 解压 zip 文件到指定目录 (防止 Zip Slip 漏洞) public static void unzip(File zipFile, File destDir) throws IOException ``` ## 哈希摘要 (Digest Utils) 支持计算 MD5, SHA-1, SHA-256 等摘要。 ```java // 计算字符串摘要 public static String getMessageDigest(String message, String algorithm) throws NoSuchAlgorithmException // 计算字节数组摘要 public static String getMessageDigest(byte[] bytes, String algorithm) throws NoSuchAlgorithmException // 计算 ByteBuffer 摘要 public static String getMessageDigest(ByteBuffer buffer, String algorithm) throws NoSuchAlgorithmException // 计算文件摘要 public static String getFileDigest(File file, String algorithm) throws IOException, NoSuchAlgorithmException // 计算 Assets 文件摘要 public static String getAssetDigest(String assetPath, String algorithm) throws IOException, NoSuchAlgorithmException // 计算 Raw 资源摘要 public static String getRawDigest(int id, String algorithm) throws IOException, NoSuchAlgorithmException // 计算流摘要 (需手动关闭流) public static String getStreamDigest(InputStream in, String algorithm) throws IOException, NoSuchAlgorithmException // 计算流摘要并自动关闭流 public static String getStreamDigestWithAutoClose(InputStream in, String algorithm) throws IOException, NoSuchAlgorithmException ``` ## 路径工具 (Path Utils) ```java // 获取路径的父目录,如果父目录为空则返回 "/" public static String getParentPath(String path) ``` --- --- url: /luajava/LuaJava.md --- # Nex LuaJava API NexLua 提供了一套 Nex LuaJava 接口(简称 nxluajava),用于与 Java 进行交互。以下是一些常用的 API。 ## luajava.bindClass(className) 类似 `Class.forName(className)`, 查找类名对应的 `Class` 对象。\ 例如 `luajava.bindClass("java.lang.String")` 返回 `String.class`。 ```lua local String = luajava.bindClass("java.lang.String") print(String) -- class java.lang.String ``` 可以使用 `void`, `boolean`, `char`, `byte`, `short`, `int`, `long`, `float`, `double` 来获取原始类型 ```lua local int = luajava.bindClass("int") print(int) -- class int ``` ## luajava.bindMethod(class, methodName \[, methodType]) 类似 `obj.getClass().getMethod(methodName, methodType)`, 查找对象的指定方法。当 `methodName` 为 `new` 时,返回构造方法。 ```lua local ArrayList = luajava.bindClass("java.util.ArrayList") local ArrayList_new = luajava.bindMethod(ArrayList, "new") local Object = luajava.bindClass("java.lang.Object") local int = luajava.bindClass("int") local ArrayList_add1 = luajava.bindMethod(ArrayList, "add", Object) local ArrayList_add2 = luajava.bindMethod(ArrayList, "add", Integer, Object) local myArray = ArrayList_new() ArrayList_add1(myArray, "Hello") ArrayList_add2(myArray, 0, "World") print(myArray.toString()) ``` ## luajava.instanceof(obj, class) 判断 obj 是否为 class 类型,返回 true 或 false。 ```lua local Button = luajava.bindClass("android.widget.Button") local btn = Button(activity) if luajava.instanceof(btn, Button) then print("btn is a Button") end ``` ## luajava.toJavaObject(value \[, class]) 将 Lua 值转换为 Java 对象。可选参数 `class` 指定目标对象类型。默认为 `Object`。 ```lua -- convert string to java.lang.String local text = "updog" local javaString = luajava.toJavaObject(t) print(javaString.getClass().getName()) -- class java.lang.String local int = luajava.bindClass("int") local float = luajava.bindClass("float") -- convert number to int or float local num = 123 local javaInt = luajava.toJavaObject(num, int) local javaFloat = luajava.toJavaObject(num, float) print(javaInt.getClass().getName()) -- class int print(javaFloat.getClass().getName()) -- class float ``` ## luajava.toJavaArray(value \[, class]) 将 Lua 值转换为 Java Array。可选参数 `class` 指定目标对象类型。默认为 `Object`。 ```lua local arr = {1, 2, 3} local javaArray1 = luajava.toJavaArray(arr) local javaArray2 = luajava.toJavaArray(arr, luajava.bindClass("float")) ``` ## luajava.toJavaMap(value \[, keyClass, valueClass]) 将 Lua 值转换为 Java Map。 可选参数 `keyClass` 和 `valueClass` 指定目标对象类型。默认为 `Object`。 ```lua local HashMap = luajava.bindClass("java.util.HashMap") local String = luajava.bindClass("java.lang.String") local int = luajava.bindClass("int") local luaMap = { "user1" = 20, "user2" = 20, "user3" = 50, "user4" = 100 } local javaMap = luajava.toJavaMap(luaMap, String, int) ``` ## luajava.toString(obj) 将 Java 对象转换为 Lua 字符串。调用的 Object 的 toString() 方法。 ```lua local Button = luajava.bindClass("android.widget.Button") print(luajava.toString(Button)) -- class android.widget.Button ``` ## luajava.asTable(obj) ```lua -- HashMap local HashMap = luajava.bindClass("java.util.HashMap") local String = luajava.bindClass("java.lang.String") local int = luajava.bindClass("int") local luaMap = { "user1" = 20, "user2" = 20, "user3" = 50, "user4" = 100 } local javaMap = luajava.toJavaMap(luaMap, String, int) -- luajava.asTable local map = luajava.asTable(javaMap) print(map["user1"]) -- 20 ``` 将 Java 对象转换为 Lua 表。 ## luajava.newInstance(clazz, object... args) 创建一个 Java 对象实例,类似 `new clazz(args)`。 ```lua local Button = luajava.bindClass("android.widget.Button") local btn = luajava.newInstance(Button, activity) ``` ## luajava.createArray(clazz, dim1 \[, dim2, ...]) 创建一个 Java 数组对象,类似 `new clazz[dim1][dim2]...`。 > 注意: Java 数组在 NexLuaJava 中使用 0 作为开始索引, 并且支持 ipairs, pairs 遍历 ```lua local String = luajava.bindClass("java.lang.String") local myArray = luajava.createArray(String, 10) local length = #myArray for i = 0, length-1 do myArray[i] = "Hello" end for key, value in pairs(myArray) do print(key, value) end for key, value in ipairs(myArray) do print(key, value) end ``` ## luajava.createProxy(clazz, handler) 创建一个 Java 代理对象,类似 `Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler)`。 ```lua local Runnable = luajava.bindClass("java.lang.Runnable") -- Use table as handler local luaRunnable1 = luajava.newProxy(Runnable, { run = function() print("Run!") end }) luaRunnable1.run() -- You can also use function as handler local luaRunnable2 = luajava.newProxy(Runnable, function() print("Here we go!") end) luaRunnable2.run() ``` ## luajava.unwrap(jobject obj) 返回 LuaProxy 代理的表或者是函数 ```lua local Runnable = luajava.bindClass("java.lang.Runnable") -- Use table as handler local proxy = luajava.newProxy(Runnable, { run = function() print("Run!") end }) local yoo1 = luajava.unwrap(proxy) print(type(yoo1)) -- table -- You can also use function as handler local luaRunnable2 = luajava.newProxy(Runnable, function() print("Here we go!") end) local yoo2 = luajava.unwrap(luaRunnable2) print(type(yoo2)) -- function ``` # NexLuaJava 语法 ## 创建 Java 对象 ```lua -- 获取类 local MyClass = luajava.bindClass("com.test.MyClass") local myObj = MyClass(1) -- 传入数字 1 作为构造参数 ``` ## 静态/实例字段读写 ```lua -- 获取类 local MyClass = luajava.bindClass("com.test.MyClass") -- 静态字段 MyClass.STATIC_FIELD = 1 print(MyClass.STATIC_FIELD) -- 实例字段 local myObj = MyClass(1) myObj.myField = 1 ``` ## 静态/实例方法调用 ```lua -- 获取类 local MyClass = luajava.bindClass("com.test.MyClass") -- 静态方法 print(MyClass.staticMethod) -- function: 0xb8a05340 MyClass.staticMethod(1) -- 实例方法 local myObj = MyClass(1) myObj.myMethod(1) ``` ## get/set 属性访问语法糖 调用 Java 的方法时,如果方法是 getXxx / setXxx 的形式,可以直接省去 get/set,并且不区分首字母的大小写 set 还可以省略末尾的 Listener ```lua -- 创建 EditText local EditText = luajava.bindClass("android.widget.EditText") local editText = EditText(activity) -- 传统代码 getText setText editText.setText("Hello, Sun!") print(editText.getText()) print(editText.isEnable()) -- get/set 语法糖 editText.Text = "Hello, Moon!" print(editText.Text) -- 忽略首字母大小写 editText.text = "Hello, World!" print(editText.text) ``` ## 数组操作 ```lua -- 创建数组 -- 创建大小为 3 的 int 类型数组,相当于 new int[3] local int = luajava.bindClass("java.lang.Integer") local myArray1 = int[3] -- 创建 int 类型数组,初始值为 1,2,3,相当于 new int[]{1, 2, 3} local myArray2 = int{1, 2, 3} -- 创建 int 类型二维数组,相当于 new int[][]{{1, 2, 3}, {10}, {20}} local myArray3 = int{{1,2,3}, {10}, {20}} -- 当然, 也可以省去花括号 local myArray3 = int{{1,2,3}, 10, 20} -- 获取数组长度 print(#myArray1) -- 获取数组长度 print(myArray1.length) -- 获取数组元素, Java 数组在 Lua 保持 0 开始 print(myArray1[0]) -- 设置数组元素 myArray1[0]=10 -- 获取二维数组元素 print(myArray1[0][1]) -- 设置二维数组元素 myArray1[0][1]=10 -- 支持 ipairs/pairs 遍历 for i, v in ipairs(myArray1) do print(i, v) end ``` --- --- url: /developer/NexLua.md --- # NexLua NexLua 使用 init.lua 作为项目的配置文件 ``` -- 名称 appname="项目名称" -- 包名 packagename="com.mycompany.testapp1" -- 版本 appcode=1 -- 版本号 appver="1.0" -- 最低 SDK 版本 appsdk="15" -- 调试模式 debugmode=true -- 应用权限 user_permissions={ "android.permission.INTERNET", "android.permission.WRITE_EXTERNAL_STORAGE", } user_routes = { ["main.lua] } ``` # NexLua ## Router ```lua return { [""] } ``` --- --- url: /nexlua/NexLua.md --- # NexLua 增强库 NexLua 在 NexLuaJava 基础上,扩展了两个核心功能库:`import` 和 `dump`。 ## import 模块 `import` 模块为 Lua 提供了类似 Java 的类导入机制,支持单个类导入和通配符包导入。若找不到类时,会 fallback 成 `require` 函数调用。 ### 导入单个类 ```lua require "import" import "java.util.ArrayList" local list = ArrayList() list.add("Hello") list.add("NexLua") print(list) ``` ### 导入整个包 使用 `.*` 结尾的字符串来导入包。这不会立即加载包下的所有类,而是注册一个搜索路径。 ```lua require "import" import "android.widget.*" import "android.view.*" local btn = Button(activity) local v = View(activity) ``` ```lua local utils = import "utils" ``` ## dump `dump` 函数用于打印 lua 中的任意值。`dump` 能够识别已访问过的 table,并避免重复打印。 ### 打印基本 table ```lua local data = { name = "NexLua", version = 1.0, features = { "Fast", "Simple" } } print(dump(data)) ``` 输出: ```lua { ["name"] = "NexLua", ["version"] = 1, ["features"] = { [1] = "Fast", [2] = "Simple" } } ``` ### 处理混合键值 ```lua local complex = { [1] = "Index 1", ["key"] = "String Key", sub = { x = 10, y = 20 } } print(dump(complex)) ``` 输出: ```lua { [1] = "Index 1", ["key"] = "String Key", ["sub"] = { ["x"] = 10, ["y"] = 20 } } ``` --- --- url: /quick-start.md --- # 什么是 NexLua? NexLua 是一个在 JVM 平台上运行的 Lua-Java 桥。允许开发者使用 NexLuajava API 访问 Java 方法/类。 ## 支持的 Lua 版本 目前只支持 LuaJIT,后续会支持其他 Lua 版本。 ## 平台 仅在安卓平台上测试通过,支持 armeabi-v7a arm64-v8a x86 x86\_64 四种架构 ## 使用 ```groovy dependencies { implementation 'com.github.justlikecheese:nexlua:LuaJava:1.1.1' } ``` ## 路线图 当前 NexLua 已实现 Lua 的所有 API,还剩下部分 Debug、Lua Alloc **辅助 API** 未实现。 ## 其他 参考了以下项目,我花了大概两个月的时间在无任何基础的情况下理解了以下框架,并重构了所有的代码。非常感谢这些先驱者的开源。这为 NexLua 的存在奠定了基础。 https://github.com/gudzpoz/luajava https://github.com/jasonsantos/luajava https://github.com/nirenr/AndroLua\_pro