【问题标题】:How do I run Lua scripts on Android in a Java application?如何在 Java 应用程序中在 Android 上运行 Lua 脚本?
【发布时间】:2012-05-15 08:18:30
【问题描述】:

我正在用 Java 开发一个使用 Lua 脚本的 Android 游戏。为了执行这些脚本,我将 LuaJ 与 Java 的 ScriptEngine 类一起使用。比如……

ScriptEngineManager sem = new ScriptEngineManager();
scriptEngine = sem.getEngineByExtension(".lua");
script = ((Compilable)scriptEngine).compile("some lua here");

但是,这显然在 Android 上不受支持(这与 android 没有完整的 JVM 有关,我在某处读到过)。有没有办法可以在 Android 上使用 Lua 脚本?也许有一个 LuaJ 替代品?也许有一种方法可以直接使用 LuaJ 编译和执行 Lua 脚本(虽然我不知道如何)。

仅供参考,当我尝试在 Android 上运行此代码时看到此错误:

05-06 16:12:32.870: E/dalvikvm(17509): Could not find class 'javax.script.ScriptEngineManager', referenced from method unearth.levels.LevelReader.<init>
05-06 16:12:32.870: W/dalvikvm(17509): VFY: unable to resolve new-instance 787 (Ljavax/script/ScriptEngineManager;) in Lunearth/levels/LevelReader;
05-06 16:12:32.870: D/dalvikvm(17509): VFY: replacing opcode 0x22 at 0x0018
05-06 16:12:32.875: E/dalvikvm(17509): Could not find class 'javax.script.Compilable', referenced from method unearth.levels.LevelReader.parseScript
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to resolve check-cast 782 (Ljavax/script/Compilable;) in Lunearth/levels/LevelReader;
05-06 16:12:32.875: D/dalvikvm(17509): VFY: replacing opcode 0x1f at 0x0047
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to resolve exception class 788 (Ljavax/script/ScriptException;)
05-06 16:12:32.875: W/dalvikvm(17509): VFY: unable to find exception handler at addr 0x51
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejected Lunearth/levels/LevelReader;.parseScript (Lorg/w3c/dom/Element;Lunearth/levels/Level;)V
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejecting opcode 0x0d at 0x0051
05-06 16:12:32.875: W/dalvikvm(17509): VFY:  rejected Lunearth/levels/LevelReader;.parseScript (Lorg/w3c/dom/Element;Lunearth/levels/Level;)V
05-06 16:12:32.875: W/dalvikvm(17509): Verifier rejected class Lunearth/levels/LevelReader;
05-06 16:12:32.890: W/dalvikvm(17509): threadid=11: thread exiting with uncaught exception (group=0x40c331f8)
05-06 16:12:32.895: E/AndroidRuntime(17509): FATAL EXCEPTION: GLThread 1062
05-06 16:12:32.895: E/AndroidRuntime(17509): java.lang.VerifyError: unearth/levels/LevelReader
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.Game.startGame(Game.java:201)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.Game.update(Game.java:713)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.screens.LoadScreen.update(LoadScreen.java:56)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at unearth.UnearthListener.render(UnearthListener.java:71)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:423)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1462)
05-06 16:12:32.895: E/AndroidRuntime(17509):    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1216)
05-06 16:12:58.600: D/dalvikvm(17509): GC_CONCURRENT freed 334K, 3% free 16196K/16647K, paused 2ms+6ms

更新:

也许这个项目有一些有用的代码? http://code.google.com/p/android-scripting/

【问题讨论】:

    标签: java android lua


    【解决方案1】:

    我找到了在 Android 上使用 LuaJ 的方法! :-)

    关键是直接使用 LuaJ API,而不是通过javax.script。由于没有教程,这有点棘手,所以这里有一个前后,这样其他人就不必通过 LuaJ 源代码和 JavaDoc 来挑选。我真的希望这对某人有所帮助,因为它让我发疯了!

    注意:“secretgame”不是我游戏的实际名称;-)

    之后:

    package secretgame.scripting;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    
    import org.luaj.vm2.LuaClosure;
    import org.luaj.vm2.LuaTable;
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.Prototype;
    import org.luaj.vm2.compiler.LuaC;
    import org.luaj.vm2.lib.jse.CoerceJavaToLua;
    import org.luaj.vm2.lib.jse.JsePlatform;
    
    import secretgame.SecretGameException;
    import secretgame.events.EventArgs;
    import secretgame.levels.Level;
    
    public class DirectLuaj implements Lua
    {
      private final Level level;
      private final ScriptTools scriptTools;
      private final ScriptEvents scriptEvents;
      private int nextId = 0;
      private ArrayList<LuaClosure> scripts = new ArrayList<LuaClosure>();
    
      public DirectLuaj(Level level, ScriptTools scriptTools,
          ScriptEvents scriptEvents)
      {
        this.level = level;
        this.scriptTools = scriptTools;
        this.scriptEvents = scriptEvents;
      }
    
      @Override
      public int add(String scriptText) throws SecretGameException
      {
        try {
          InputStream input = new ByteArrayInputStream(scriptText.getBytes());
          Prototype p = LuaC.compile(input, "script");
          LuaValue g = JsePlatform.standardGlobals();
          LuaClosure c = new LuaClosure(p, g);
          scripts.add(c);
        }
        catch (IOException e) {
          throw new SecretGameException("compile failed", e);
        }
    
        return nextId++;
      }
    
      @Override
      public void run(int id, EventArgs args) throws SecretGameException
      {
        LuaClosure script = scripts.get(id);
    
        LuaTable bindings = new LuaTable();
    
        bindings.set("java", toLua(scriptTools));
        bindings.set("level", toLua(level));
        bindings.set("args", toLua(args));
        bindings.set("events", toLua(scriptEvents));
    
        script.setfenv(bindings);
    
        script.call();
      }
    
      private LuaValue toLua(Object javaValue) {
        return javaValue == null? LuaValue.NIL:
                javaValue instanceof LuaValue? (LuaValue) javaValue:
                CoerceJavaToLua.coerce(javaValue);
      }
    }
    

    之前:

    package secretgame.scripting;
    
    import java.util.ArrayList;
    
    import javax.script.Bindings;
    import javax.script.Compilable;
    import javax.script.CompiledScript;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    
    import secretgame.SecretGameException;
    import secretgame.events.EventArgs;
    import secretgame.levels.Level;
    
    // sadly this won't work on Android because there's no such thing
    // as javax.script in Dalvik ... dumb Android java :|
    public class JavaxScriptingLua implements Lua
    {
      private ScriptEngine scriptEngine;
      private final Level level;
      private final ScriptTools scriptTools;
      private final ScriptEvents scriptEvents;
      private int nextId = 0;
      private ArrayList<CompiledScript> scripts = new ArrayList<CompiledScript>();
    
      public JavaxScriptingLua(Level level, ScriptTools scriptTools, ScriptEvents scriptEvents)
      {
        this.level = level;
        this.scriptTools = scriptTools;
        this.scriptEvents = scriptEvents;
    
        ScriptEngineManager sem = new ScriptEngineManager();
        scriptEngine = sem.getEngineByExtension(".lua");
      }
    
      public int add(String scriptText) throws SecretGameException
      {
        try {
          CompiledScript script = ((Compilable)scriptEngine).compile(scriptText);
          scripts.add(script);
        }
        catch (ScriptException e) {
          throw new SecretGameException("could not compile lua.", e);
        }
    
        return nextId++;
      }
    
      public void run(int id, EventArgs args) throws SecretGameException
      {    
        Bindings bindings = scriptEngine.createBindings();
    
        bindings.put("java", scriptTools);
        bindings.put("level", level);
        bindings.put("args", args);
        bindings.put("events", scriptEvents);
    
        try {
          scripts.get(id).eval(bindings);
        }
        catch (ScriptException e) {
          throw new SecretGameException("could not run script.", e);
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      除了 LuaJ,您还可以(也)将 LuaJava 与使用 NDK 为 Android 编译的 Lua 一起使用。这允许 Lua 完全访问 Java 中可用的任何内容。查看this example 了解如何开始。

      Lua 5.1.4 + LuaJava 编译后在libs 目录(单个libluajava.so 文件)中提供,如果您不想摆弄NDK,但无论如何设置相对容易。

      【讨论】:

      • 对 AndroLua 的赞誉,但我找到了一种在 Android 上使用 LuaJ 的方法,因此我不必重新编写现有代码。请参阅我的答案,了解我是如何解决问题的。
      【解决方案3】:

      要在 Android 上使用 Lua,您可以使用 JNLua + Lua 5.2(用 C 实现)。唯一支持 JSR 223 (javax.script) 的 Android 端口在这里:https://github.com/danke-sra/jnlua-android

      使用这个端口,你可以做你想做的事:

      ScriptEngine scriptEngine = new LuaScriptEngineFactory().getEngine(); scriptEngine.eval("lua statements");

      【讨论】:

        【解决方案4】:

        但是,Android 显然不支持此功能

        正确。

        与 android 没有完整的 JVM 有关,我在某处读到

        正确。如果您查看the JavaDocs,您会发现Android SDK 中不存在javax.script

        也许我会尝试一下:https://github.com/damonkohler/sl4a

        这是一种选择。另一种方法是通过 NDK 将 Lua 解释器嵌入到您的应用中,如 this sample application

        【讨论】:

        • 感谢您的回答——但我还是设法让 LuaJ 在 Android 上运行。有兴趣可以看我的回答。
        猜你喜欢
        • 2014-01-02
        • 2015-08-17
        • 1970-01-01
        • 2012-02-21
        • 1970-01-01
        • 1970-01-01
        • 2013-10-17
        • 2016-03-29
        • 1970-01-01
        相关资源
        最近更新 更多