折腾笔记[46]-在安卓手机运行基于java8的lua解释器

摘要

不走寻常路, 在安卓手机通过java虚拟机PojavLauncher运行基于java8的lua解释器.

Abstract

Running a Java 8-based Lua interpreter on Android phones through the Java virtual machine PojavLauncher;

AGENTS.md声明

本文用于投喂龙虾(openclaw).

关键信息

  1. java8 (jdk 1.8)
  2. gradle 8.5
  3. swing
  4. gradle
  5. luaj
  6. rsyntaxtextarea

简介

LuaDroid简介

[https://play.google.com/store/apps/details?id=com.alif.ide.lua&hl=zh_CN]

  • LuaDroid - ide for lua

LuaDroid is an integrated development environment (IDE) designed for creating Lua scripts directly on your Android device.

J2ME Loader简介

[https://github.com/nikita36078/J2ME-Loader]
J2ME Loader是一款开源的Android平台J2ME(Java 2 Micro Edition)模拟器,由Nikita Shakarun开发。它允许用户在现代安卓设备上运行绝大多数2D及部分3D(不支持Mascot Capsule)的经典JAR游戏和应用,支持自定义虚拟键盘、屏幕缩放和屏幕尺寸设置,是重温诺基亚老游戏的最佳工具之一。

PojavLauncher简介

[https://github.com/PojavLauncherTeam/PojavLauncher]
[https://github.com/AngelAuraMC/Amethyst-Android]
PojavLauncher 是一款基于 Boardwalk 开发的、开源的、适用于 Android 和 iOS 的《我的世界》Java 版移动端启动器。它能让用户在手机上游玩几乎所有版本的 Minecraft Java 版(包含支持最新的 Java 21),支持离线和正版登录。
主要特色与功能:

  • 移动端运行 Java 版: 可以在安卓/iOS设备上无缝体验原汁原味的沙盒冒险。
  • 多版本切换: 支持从旧版本到最新版本的Java版游戏。
  • 模组安装: 内置高度自由度,允许安装各类模组(如NPC、任务、道具)。
  • 自定义控制: 支持高度可自定义的触屏控制键、背景音乐和标题。
  • 在线体验: 支持自定义服务器IP和端口,确保多人协作与在线交互。

Java Runtime Environment (JRE): Download the jre8-pojav artifact from our CI auto builds. This package contains pre-built JREs for all supported architectures. If you need to build the JRE yourself, follow the instructions in the android-openjdk-build-multiarch repository.

fatjar简介

Fat JAR(也称Uber-JAR)是一种包含Java应用程序所有类、资源以及第三方依赖库的归档文件(JAR)。相比普通JAR,它“胖”在将所有依赖组件打包在一起,实现了“一次打包,处处运行”。它主要用于简化项目部署,直接用java -jar运行,极其方便。
核心特性与优势:

  • all-in-one(一体化): 将业务代码和依赖项合并,无需处理复杂的CLASSPATH配置。
  • 部署简便: 只需要分发一个jar文件。
  • 适用场景: 非常适合微服务、命令行工具和容器化部署(如Docker),符合Java开发中便捷发布的需求。
  • 构建方式: 常用 Maven(使用 maven-shade-plugin 或 maven-assembly-plugin)或 Gradle 工具生成。

luaj简介

[https://github.com/luaj/luaj]
Luaj 是一个基于 Java 实现的轻量级、高性能 Lua 解释器与虚拟机,主要用于在 Java 应用(SE、EE、ME)中无缝集成 Lua 脚本。它基于 Lua 5.2.x 版本,允许 Java 与 Lua 互相调用、互操作,广泛应用于游戏逻辑、动态扩展和配置。
核心特征:

  • Java 原生实现: 无需 JNI(Java Native Interface),直接在 Java 虚拟机中运行,易于跨平台部署。
  • 轻量高性能: 占用资源少,执行速度快。
  • 互操作性: 支持 Java 调用 Lua 函数,以及 Lua 调用 Java 的静态方法和类。
  • 完整库支持: 提供 Lua 5.2.x 标准的库,兼容性好。

Lightweight, fast, Java-centric Lua interpreter written for JME and JSE, with string, table, package, math, io, os, debug, coroutine & luajava libraries, JSR-223 bindings, all metatags, weak tables and unique direct lua-to-java-bytecode compiling.

rsyntaxtextarea简介

[https://github.com/bobbylight/RSyntaxTextArea]
RSyntaxTextArea是一个功能强大、开源的Java Swing文本编辑器组件,专为需要语法高亮和代码折叠功能的应用程序设计。它支持超过50种编程语言,并具备代码补全、搜索替换、行号显示等类似IDE的功能,是构建Java桌面应用代码编辑器的理想选择。

以下是RSyntaxTextArea的核心特点:

  • 语法高亮:支持50多种主流编程语言,可高度定制。
  • 代码折叠:支持代码块折叠与展开,方便查看结构。
  • 高级编辑功能:包括行号、标记当前行、搜索和替换、撤销/重做。
  • 扩展性强:结合AutoComplete和SpellChecker库,可实现代码补全和拼写检查。
  • 易于集成:基于Java Swing框架,可直接嵌入到Java桌面应用中。
    该组件通过性能优化和不断增加新语言支持,已成为Java Swing开发中进行代码编辑的常用组件。

A syntax highlighting, code folding text editor for Java Swing applications.

实现

1. 安装jdk8

[https://openjdk.org/]
[https://github.com/ojdkbuild/ojdkbuild/]
[https://github.com/ojdkbuild/ojdkbuild/releases/download/java-1.8.0-openjdk-1.8.0.322-1.b06-x86/java-1.8.0-openjdk-1.8.0.322-1.b06.ojdkbuild.windows.x86.msi]
[https://github.com/openjdk/jdk]
[https://github.com/ojdkbuild/ojdkbuild/releases/tag/java-1.8.0-openjdk-1.8.0.332-1.b09-x86]
[https://www.azul.com/downloads/]
[https://www.azul.com/downloads/#zulu]

  • 记得设置JAVA_HOME系统变量

2. 构建程序

项目结构

.
├── build.gradle              # Gradle 构建配置
├── settings.gradle           # Gradle 设置
├── gradlew                   # Gradle 包装脚本
├── gradle/wrapper/           # Gradle 包装文件
├── src/main/java/
│   ├── HelloLua.java         # 控制台示例
│   └── LuaEditor.java        # GUI 编辑器(RSyntaxTextArea)
└── README.md

关键代码

build.gradle

plugins {
    id 'java'
    id 'application'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.luaj:luaj-jse:3.0.1'
    implementation 'com.fifesoft:rsyntaxtextarea:3.3.4'
}

application {
    mainClass = 'LuaEditor'
}

jar {
    manifest {
        attributes 'Main-Class': 'LuaEditor'
    }
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    // 将字体文件打包到 JAR 根目录
    from('SmileySans-Oblique.ttf') {
        into ''
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

tasks.register('fatjar', Jar) {
    group = 'build'
    description = 'Creates a fat JAR with all dependencies'
    archiveClassifier = 'fat'
    
    manifest {
        attributes 'Main-Class': 'LuaEditor'
    }
    
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    
    // 将字体文件打包到 JAR 根目录
    from('SmileySans-Oblique.ttf') {
        into ''
    }
    
    with jar
    
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    
    destinationDirectory = file("${buildDir}/fatjar")
}

settings.gradle

rootProject.name = 'java-lua-hello'

src/main/java/HelloLua.java

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

/**
 * HelloLua - LuaJ 控制台示例程序
 * 
 * 演示内容:
 * 1. 基本的 Lua 脚本执行
 * 2. 获取 Lua 返回值
 * 3. 定义和调用 Lua 函数
 * 4. Java 与 Lua 互操作
 */
public class HelloLua {
    public static void main(String[] args) {
        // 创建 Lua 全局环境(包含标准库)
        Globals globals = JsePlatform.standardGlobals();

        // ========== 示例 1: 简单的 Hello World ==========
        System.out.println("=== Lua 入门示例 ===\n");
        
        // 加载并执行 Lua 代码
        LuaValue chunk = globals.load("print '你好,来自 Lua 的世界!'");
        chunk.call();

        // ========== 示例 2: 数学运算 ==========
        System.out.println("\n--- 数学运算示例 ---");
        // 执行表达式并获取返回值
        chunk = globals.load("return 10 + 20 * 2");
        LuaValue result = chunk.call();
        System.out.println("10 + 20 * 2 = " + result.toint());

        // ========== 示例 3: 函数定义与调用 ==========
        System.out.println("\n--- 函数示例 ---");
        // 在 Lua 中定义阶乘函数
        globals.load(
            "function factorial(n)\n" +
            "    if n <= 1 then return 1 end\n" +
            "    return n * factorial(n - 1)\n" +
            "end\n"
        ).call();

        // 从 Java 中获取 Lua 函数并调用
        LuaValue factorialFunc = globals.get("factorial");
        LuaValue factResult = factorialFunc.call(LuaValue.valueOf(5));
        System.out.println("factorial(5) = " + factResult.toint());

        // ========== 示例 4: Java 与 Lua 互操作 ==========
        System.out.println("\n--- Java 互操作示例 ---");
        // 在 Lua 中访问 Java 类
        chunk = globals.load(
            "local Math = luajava.bindClass('java.lang.Math')\n" +
            "print('Java Math.PI = ' .. Math.PI)\n" +
            "print('sqrt(16) = ' .. Math:sqrt(16))\n" +
            "return Math:max(10, 25)\n"
        );
        LuaValue maxResult = chunk.call();
        System.out.println("max(10, 25) = " + maxResult.toint());

        System.out.println("\n=== 完成 ===");
    }
}

src/main/java/LuaEditor.java

import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;

/**
 * Lua 编辑器 - 使用 LuaJ + RSyntaxTextArea 的 Swing GUI 编辑器
 * 
 * 功能:
 * - Lua 语法高亮显示
 * - 代码折叠
 * - 运行 Lua 脚本
 * - 加载/保存 .lua 文件
 * - 快捷键支持 (Ctrl+R 运行)
 */
public class LuaEditor extends JFrame {
    private RSyntaxTextArea codeArea;      // 代码编辑区域
    private JTextArea outputArea;          // 输出显示区域
    private Globals globals;               // Lua 全局环境
    private Font customFont;               // 自定义中文字体

    public LuaEditor() {
        // 加载内嵌字体
        loadEmbeddedFont();
        // 设置窗口标题
        setTitle("Lua 编辑器 - LuaJ + RSyntaxTextArea");
        // 关闭窗口时退出程序
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口大小
        setSize(900, 700);
        // 窗口居中显示
        setLocationRelativeTo(null);

        // 初始化 Lua 全局环境(包含标准库)
        globals = JsePlatform.standardGlobals();

        // 将 Lua 的 print 函数重定向到我们的输出区域
        globals.set("print", new PrintFunction());

        // 初始化界面组件
        initComponents();
    }

    /**
     * 加载内嵌的中文字体
     */
    private void loadEmbeddedFont() {
        try {
            // 从 JAR 内加载字体文件
            URL fontUrl = getClass().getResource("/SmileySans-Oblique.ttf");
            if (fontUrl != null) {
                customFont = Font.createFont(Font.TRUETYPE_FONT, fontUrl.openStream());
                // 注册字体到图形环境
                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
                ge.registerFont(customFont);
                System.out.println("成功加载内嵌字体: SmileySans-Oblique.ttf");
            } else {
                System.err.println("警告: 未找到内嵌字体文件,使用系统默认字体");
                customFont = new Font(Font.MONOSPACED, Font.PLAIN, 14);
            }
        } catch (Exception e) {
            System.err.println("加载字体失败: " + e.getMessage());
            customFont = new Font(Font.MONOSPACED, Font.PLAIN, 14);
        }
    }

    /**
     * 获取指定大小的自定义字体
     */
    private Font getCustomFont(float size) {
        if (customFont != null) {
            return customFont.deriveFont(size);
        }
        return new Font(Font.MONOSPACED, Font.PLAIN, (int) size);
    }

    /**
     * 初始化界面组件
     */
    private void initComponents() {
        setLayout(new BorderLayout(5, 5));

        // === 代码编辑面板 ===
        codeArea = new RSyntaxTextArea(20, 60);
        // 设置 Lua 语法高亮
        codeArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_LUA);
        // 启用代码折叠
        codeArea.setCodeFoldingEnabled(true);
        // 启用抗锯齿
        codeArea.setAntiAliasingEnabled(true);
        // 设置自定义字体(支持中文)
        codeArea.setFont(getCustomFont(14f));
        // 设置默认代码示例
        codeArea.setText(
            "-- 欢迎使用 Lua 编辑器\n" +
            "-- 在这里编写 Lua 代码,然后点击运行\n\n" +
            "-- 定义一个函数\n" +
            "function greet(name)\n" +
            "    return \"你好,\" .. name .. \"!\"\n" +
            "end\n\n" +
            "-- 调用函数并输出\n" +
            "print(greet(\"世界\"))\n\n" +
            "-- 循环示例\n" +
            "for i = 1, 5 do\n" +
            "    print(\"计数: \" .. i)\n" +
            "end\n\n" +
            "-- Java 互操作示例\n" +
            "local Math = luajava.bindClass('java.lang.Math')\n" +
            "print('Java PI = ' .. Math.PI)\n"
        );

        // 代码滚动面板
        RTextScrollPane codeScrollPane = new RTextScrollPane(codeArea);
        codeScrollPane.setBorder(BorderFactory.createTitledBorder(
            new EtchedBorder(), "Lua 代码", TitledBorder.LEFT, TitledBorder.TOP));

        // === 输出面板 ===
        RTextArea outputTextArea = new RTextArea(10, 60);
        outputTextArea.setEditable(false);
        outputTextArea.setFont(getCustomFont(13f));
        // 深色背景
        outputTextArea.setBackground(new Color(30, 30, 30));
        // 浅色文字
        outputTextArea.setForeground(new Color(200, 200, 200));
        outputArea = outputTextArea;

        RTextScrollPane outputScrollPane = new RTextScrollPane(outputTextArea);
        outputScrollPane.setBorder(BorderFactory.createTitledBorder(
            new EtchedBorder(), "输出", TitledBorder.LEFT, TitledBorder.TOP));

        // === 分割面板(上下分割) ===
        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, codeScrollPane, outputScrollPane);
        // 设置分割比例(代码区占 60%)
        splitPane.setResizeWeight(0.6);
        add(splitPane, BorderLayout.CENTER);

        // === 工具栏 ===
        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);
        // 设置工具栏字体
        toolBar.setFont(getCustomFont(13f));

        // 运行按钮
        JButton runButton = new JButton("▶ 运行");
        runButton.setFont(getCustomFont(13f));
        runButton.setToolTipText("运行 Lua 脚本 (Ctrl+R)");
        runButton.addActionListener(e -> runScript());

        // 清空输出按钮
        JButton clearButton = new JButton("🗑 清空输出");
        clearButton.setFont(getCustomFont(13f));
        clearButton.addActionListener(e -> outputArea.setText(""));

        // 加载文件按钮
        JButton loadButton = new JButton("📂 打开");
        loadButton.setFont(getCustomFont(13f));
        loadButton.addActionListener(e -> loadFile());

        // 保存文件按钮
        JButton saveButton = new JButton("💾 保存");
        saveButton.setFont(getCustomFont(13f));
        saveButton.addActionListener(e -> saveFile());

        // 添加按钮到工具栏
        toolBar.add(runButton);
        toolBar.addSeparator();
        toolBar.add(loadButton);
        toolBar.add(saveButton);
        toolBar.addSeparator();
        toolBar.add(clearButton);

        add(toolBar, BorderLayout.NORTH);

        // === 状态栏 ===
        JLabel statusLabel = new JLabel(" 就绪");
        statusLabel.setFont(getCustomFont(12f));
        statusLabel.setBorder(BorderFactory.createLoweredBevelBorder());
        add(statusLabel, BorderLayout.SOUTH);

        // === 键盘快捷键 ===
        InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = getRootPane().getActionMap();

        // Ctrl+R 运行脚本
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK), "run");
        actionMap.put("run", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                runScript();
            }
        });
    }

    /**
     * 运行 Lua 脚本
     */
    private void runScript() {
        String code = codeArea.getText();
        outputArea.setText("");
        outputArea.append("=== 正在运行 Lua 脚本 ===\n\n");

        try {
            // 加载并执行 Lua 代码
            LuaValue chunk = globals.load(code);
            Varargs result = chunk.invoke();
            
            // 显示返回值
            if (result.narg() > 0 && !result.arg1().isnil()) {
                outputArea.append("\n=== 返回值 ===\n");
                for (int i = 1; i <= result.narg(); i++) {
                    outputArea.append(result.arg(i).toString() + "\n");
                }
            }
            outputArea.append("\n=== 脚本执行成功 ===");
        } catch (LuaError e) {
            // 显示错误信息
            outputArea.append("\n=== 错误 ===\n");
            outputArea.append(e.getMessage() + "\n");
        }
    }

    /**
     * 加载 Lua 文件
     */
    private void loadFile() {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Lua 文件 (*.lua)", "lua"));
        
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                StringBuilder content = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
                codeArea.setText(content.toString());
                setTitle("Lua 编辑器 - " + file.getName());
            } catch (IOException e) {
                JOptionPane.showMessageDialog(this, 
                    "加载文件失败: " + e.getMessage(), 
                    "错误", 
                    JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * 保存 Lua 文件
     */
    private void saveFile() {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Lua 文件 (*.lua)", "lua"));
        
        if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            // 自动添加 .lua 扩展名
            if (!file.getName().endsWith(".lua")) {
                file = new File(file.getAbsolutePath() + ".lua");
            }
            try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
                writer.print(codeArea.getText());
                setTitle("Lua 编辑器 - " + file.getName());
            } catch (IOException e) {
                JOptionPane.showMessageDialog(this, 
                    "保存文件失败: " + e.getMessage(), 
                    "错误", 
                    JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * 自定义 print 函数,将输出重定向到输出区域
     */
    private class PrintFunction extends org.luaj.vm2.lib.VarArgFunction {
        @Override
        public Varargs invoke(Varargs args) {
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= args.narg(); i++) {
                if (i > 1) sb.append("\t");
                sb.append(args.arg(i).toString());
            }
            outputArea.append(sb.toString() + "\n");
            return NONE;
        }
    }

    public static void main(String[] args) {
        // 设置系统外观
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 在事件调度线程中创建和显示 GUI
        SwingUtilities.invokeLater(() -> {
            new LuaEditor().setVisible(true);
        });
    }
}

3. 效果

macOS android
截屏2026-03-15 16.26 1000151827
posted @ 2026-03-15 17:20  qsBye  阅读(1)  评论(0)    收藏  举报