
Creo que muchos lectores del centro de desarrollo de Android han escuchado que Java le permite modificar la destreza de una aplicación ya instalada en
tiempo de
ejecución a través de
ClassLoader . Con esto, puede cargar el código compilado en tiempo de ejecución y usarlo. Pero Google trata tales fraudes, por decirlo suavemente, no demasiado leal y prohíbe a los atrapados en una aplicación de este tipo.
Sin embargo, hay formas alternativas de descargar y ejecutar scripts en un dispositivo móvil. Para detalles debajo del gato!
Entonces, aunque no podemos actualizar las aplicaciones dex en tiempo de ejecución, podemos usar intérpretes de lenguajes de script que están escritos completamente en Java. Entonces, Oracle, comenzando con la versión 6, incluye el motor jhascript Rhino en la JVM. Esto sucedió gracias a la implementación de la especificación JSR-223, que declara el soporte de Java para los lenguajes de programación de scripts.
Actualmente, hay varios motores integrados para lenguajes de programación tan populares como: Lua (Luaj), Python (Jython), Ruby (Jruby) y java-script (Rhino, ...). Cada uno de ellos le permite ejecutar scripts y acceder a funciones escritas en Java.
Como demostración de las posibilidades, propongo implementar un “entorno” de desarrollo. Dejaré un enlace a las fuentes al final del artículo. Para no saturar el ejemplo, me centraré en Lua, aunque no hay nada que le impida conectar todos los motores al mismo tiempo y cambiar entre ellos. La versión actual de JLua en el momento de la escritura está disponible en mvnrepository:
org.luaj: luaj-jse: 3.0.1 .
Cada entorno de desarrollo respetuoso debe tener un campo para ingresar un script, un campo para mostrar el resultado y un botón que le permita completar su creación.
Interfaz de usuario de un entorno de desarrollo respetuoso:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/scriptInput" android:layout_width="0dp" android:layout_height="0dp" android:gravity="top|start" android:hint="@string/write_script" android:inputType="textMultiLine" android:padding="4dp" android:textColor="#000000" app:layout_constraintBottom_toTopOf="@id/scriptOutput" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/scriptOutput" android:layout_width="0dp" android:layout_height="0dp" android:hint="@string/script_output" android:padding="4dp" android:textColor="#000000" app:layout_constraintBottom_toTopOf="@id/executeButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/scriptInput" /> <Button android:id="@+id/executeButton" android:layout_width="0dp" android:layout_height="48dp" android:text="@string/run_script" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Para ejecutar el script Lua, necesitamos obtener el entorno global en el que se ejecutará:
Globals . Luaj le permite personalizarlo, por ejemplo, configurando variables o agregando carpetas a las clases Java. Una oportunidad importante para nosotros aquí será establecer flujos de salida de mensajes, porque
java.lang.System.out ,
java.lang.System.err se usa de forma predeterminada, lo que no es muy conveniente cuando necesita mostrar el resultado de la ejecución en TextView. Para cambiar esto, debe redefinir los valores
Globals # STDOUT y
Globals # STDERR .
Por lo tanto, ahora solo tenemos que cargar nuestro crujido en el entorno y ejecutarlo.
Así es como se ve en mi ejemplo:
private fun runLua(script: String) { val charset = StandardCharsets.UTF_8 val globals = JsePlatform.standardGlobals() val outStream = ByteArrayOutputStream() val outPrintStream = PrintStream(outStream, true, charset.name()) globals.STDOUT = outPrintStream globals.STDERR = outPrintStream try { globals.load(script).call() scriptOutput.setTextColor(Color.BLACK) scriptOutput.text = String(outStream.toByteArray(), charset) } catch (e: LuaError) { scriptOutput.setTextColor(Color.RED) scriptOutput.text = e.message } finally { outPrintStream.close() } }
Ahora intentemos expandir el conjunto de funciones disponibles con la capacidad de mostrar
Toast usando el enlace anterior de las clases Java. Hazlo fácil usando
CoerceJavaToLua :
globals.set("bubble", CoerceJavaToLua.coerce(Bubble(this))) ... private class Bubble(private val context: Context) {
El resultado que obtuve es este:

Por lo tanto, con un pequeño ejemplo, consideramos la posibilidad de ejecutar scripts dentro de una aplicación móvil. Un lector inquisitivo puede adivinar que los scripts se pueden descargar desde activos, recursos de aplicaciones o desde el servidor. Lo que puede ser útil, por ejemplo, en los juegos. Afortunadamente, luaj es compatible con uno de los frameworks de juegos más populares de Java: Libgdx. En general, el alcance aquí está limitado solo por la imaginación del desarrollador.
→
Fuentes de muestra→
Luaj→
Jython→
Jruby→
Rhino (
contenedor de Android )