在Java应用程序中,如何在Android上运行Lua脚本?

时间:2021-11-23 07:32:37

I'm developing an Android game in Java which uses Lua scripts. To execute these scripts, I'm using LuaJ with Java's ScriptEngine class. For example...

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

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

However, this is apparently not supported on Android (something to do with android not having a full blown JVM, I read somewhere). Is there a way I can use Lua scripts on Android? Maybe there's a LuaJ alternative? Perhaps there is a way of compiling and executing Lua scripts using LuaJ directly (though I can't see how).

不过,Android显然不支持这一点(我在某处读到过,这与Android没有完全成熟的JVM有关)。有没有办法在Android上使用Lua脚本?也许有LuaJ的替代品?也许有一种直接使用LuaJ编译和执行Lua脚本的方法(尽管我看不出是怎么做的)。

FYI, I see this error when I try to run this code on Android:

顺便说一下,我在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

Update:

Maybe this project has some useful code? http://code.google.com/p/android-scripting/

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

4 个解决方案

#1


11  

I found a way of using LuaJ on Android! :-)

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

The key is to use the LuaJ API directly as opposed to through javax.script. It's a little tricky to figure out as there's no tutorials, so here's a before and after so that others don't have to go through picking through the LuaJ source code and JavaDoc. I really hope this helps someone as it drove me bonkers!

关键是直接使用LuaJ API,而不是通过javax.script。由于没有教程,所以要弄清楚这一点有点麻烦,所以这里有一个before和after,这样其他人就不必仔细阅读LuaJ源代码和JavaDoc。我真的希望这能帮助别人,因为它让我发疯!

Note: "secretgame" isn't the actual name of my game ;-)

注意:“秘密游戏”不是我游戏的真实名称;

After:

后:

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);
  }
}

Before:

之前:

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


4  

Instead of LuaJ you can (also) use LuaJava together with Lua compiled for Android using the NDK. This allows Lua full access to whatever is available from Java. Take a look at this example to see how to get started.

除了LuaJ之外,您还可以使用LuaJava和使用NDK为Android编译的Lua。这允许Lua完全访问Java中可用的任何东西。看看这个例子,看看如何开始。

Lua 5.1.4 + LuaJava compiled are provided in the libs directory (a single libluajava.so file) if you do not want to fiddle with the NDK, but it is relatively easy to set up anyway.

在libs目录(一个单一的libluajava)中提供了Lua 5.1.4 + LuaJava编译。所以文件)如果你不想摆弄NDK,但它是相对容易设置。

#3


1  

However, this is apparently not supported on Android

然而,Android显然不支持这一点

Correct.

正确的。

something to do with android not having a full blown JVM, I read somewhere

我在什么地方读到过,这与android没有完全成熟的JVM有关

Correct. If you look at the JavaDocs, you will see that javax.script does not exist in the Android SDK.

正确的。如果您查看JavaDocs,您将看到那个javax。Android SDK中不存在脚本。

Maybe I'll give this a try: https://github.com/damonkohler/sl4a

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

That is one option. Another is to embed the Lua interpreter in your app via the NDK, as in this sample application.

这是一个选择。另一种方法是通过NDK在应用程序中嵌入Lua解释器,就像这个示例应用程序一样。

#4


0  

To use Lua on Android, you can use JNLua + Lua 5.2 (implemented in C). The only Android port with JSR 223 (javax.script) support is here: https://github.com/danke-sra/jnlua-android

要在Android上使用Lua,可以使用JNLua + Lua 5.2(在C中实现)

Using this port, you can do what you want:

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

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

新卢阿斯替尼工厂()。getengine ();scriptEngine。eval(“lua声明”);

#1


11  

I found a way of using LuaJ on Android! :-)

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

The key is to use the LuaJ API directly as opposed to through javax.script. It's a little tricky to figure out as there's no tutorials, so here's a before and after so that others don't have to go through picking through the LuaJ source code and JavaDoc. I really hope this helps someone as it drove me bonkers!

关键是直接使用LuaJ API,而不是通过javax.script。由于没有教程,所以要弄清楚这一点有点麻烦,所以这里有一个before和after,这样其他人就不必仔细阅读LuaJ源代码和JavaDoc。我真的希望这能帮助别人,因为它让我发疯!

Note: "secretgame" isn't the actual name of my game ;-)

注意:“秘密游戏”不是我游戏的真实名称;

After:

后:

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);
  }
}

Before:

之前:

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


4  

Instead of LuaJ you can (also) use LuaJava together with Lua compiled for Android using the NDK. This allows Lua full access to whatever is available from Java. Take a look at this example to see how to get started.

除了LuaJ之外,您还可以使用LuaJava和使用NDK为Android编译的Lua。这允许Lua完全访问Java中可用的任何东西。看看这个例子,看看如何开始。

Lua 5.1.4 + LuaJava compiled are provided in the libs directory (a single libluajava.so file) if you do not want to fiddle with the NDK, but it is relatively easy to set up anyway.

在libs目录(一个单一的libluajava)中提供了Lua 5.1.4 + LuaJava编译。所以文件)如果你不想摆弄NDK,但它是相对容易设置。

#3


1  

However, this is apparently not supported on Android

然而,Android显然不支持这一点

Correct.

正确的。

something to do with android not having a full blown JVM, I read somewhere

我在什么地方读到过,这与android没有完全成熟的JVM有关

Correct. If you look at the JavaDocs, you will see that javax.script does not exist in the Android SDK.

正确的。如果您查看JavaDocs,您将看到那个javax。Android SDK中不存在脚本。

Maybe I'll give this a try: https://github.com/damonkohler/sl4a

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

That is one option. Another is to embed the Lua interpreter in your app via the NDK, as in this sample application.

这是一个选择。另一种方法是通过NDK在应用程序中嵌入Lua解释器,就像这个示例应用程序一样。

#4


0  

To use Lua on Android, you can use JNLua + Lua 5.2 (implemented in C). The only Android port with JSR 223 (javax.script) support is here: https://github.com/danke-sra/jnlua-android

要在Android上使用Lua,可以使用JNLua + Lua 5.2(在C中实现)

Using this port, you can do what you want:

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

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

新卢阿斯替尼工厂()。getengine ();scriptEngine。eval(“lua声明”);