Harmonie der Skripte in der Android-Anwendung



Ich denke, viele Leser des Android Development Hub haben gehört, dass Sie mit Java den Dex einer bereits installierten Anwendung zur Laufzeit über ClassLoader ändern können . Auf diese Weise können Sie kompilierten Code zur Laufzeit laden und verwenden. Aber Google behandelt solche Betrugsfälle, gelinde gesagt, nicht zu loyal und verbietet diejenigen, die in einer solchen Anwendung gefangen sind.

Es gibt jedoch alternative Möglichkeiten zum Herunterladen und Ausführen von Skripten auf einem mobilen Gerät. Für Details unter der Katze!

Obwohl wir Dex-Anwendungen zur Laufzeit nicht aktualisieren können, können wir Interpreter von Skriptsprachen verwenden, die vollständig in Java geschrieben sind. Daher enthält Oracle ab Version 6 die Jhascript-Engine Rhino in der JVM. Dies geschah dank der Implementierung der JSR-223-Spezifikation, die die Unterstützung für Skriptprogrammiersprachen in Java erklärt.

Derzeit gibt es mehrere integrierte Engines für so beliebte Programmiersprachen wie Lua (Luaj), Python (Jython), Ruby (Jruby) und Java-Script (Rhino, ...). Mit jedem von ihnen können Sie sowohl Skripte ausführen als auch auf in Java geschriebene Funktionen zugreifen.
Als Demonstration der Möglichkeiten schlage ich vor, eine Entwicklungsumgebung zu implementieren. Ich werde am Ende des Artikels einen Link zu den Quellen hinterlassen. Um das Beispiel nicht zu überladen, werde ich mich auf Lua konzentrieren, obwohl nichts Sie daran hindert, alle Motoren gleichzeitig anzuschließen und zwischen ihnen zu wechseln. Die aktuelle Version von JLua zum Zeitpunkt des Schreibens ist im mvnrepository verfügbar: org.luaj: luaj-jse: 3.0.1 .
Jede sich selbst respektierende Entwicklungsumgebung sollte ein Feld zum Eingeben eines Skripts, ein Feld zum Anzeigen des Ergebnisses und eine Schaltfläche zum Vervollständigen Ihrer Idee haben.

Benutzeroberfläche einer sich selbst respektierenden Entwicklungsumgebung:

<?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> 

Um das Lua-Skript auszuführen, müssen wir die globale Umgebung abrufen, in der es ausgeführt wird - Globals . Mit Luaj können Sie es anpassen, indem Sie beispielsweise Variablen festlegen oder Java-Klassen Ordner hinzufügen. Eine wichtige Gelegenheit für uns ist hier das Festlegen von Nachrichtenausgabestreams, da standardmäßig java.lang.System.out , java.lang.System.err verwendet wird. Dies ist nicht sehr praktisch, wenn Sie das Ergebnis der Ausführung in TextView anzeigen müssen. Um dies zu ändern, müssen Sie die Werte Globals # STDOUT und Globals # STDERR neu definieren .

Daher müssen wir jetzt nur noch unser Knarren in die Umgebung laden und ausführen.

So sieht es in meinem Beispiel aus:

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

Versuchen wir nun, den Satz verfügbarer Funktionen zu erweitern und Toast mithilfe der obigen Bindung von Java-Klassen anzuzeigen. Machen Sie es sich mit CoerceJavaToLua einfach:

 globals.set("bubble", CoerceJavaToLua.coerce(Bubble(this))) ... private class Bubble(private val context: Context) { // called from lua fun show(message: String) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } 

Das Ergebnis ist:



Mit einem kleinen Beispiel haben wir daher die Möglichkeit in Betracht gezogen, Skripte in einer mobilen Anwendung auszuführen. Ein neugieriger Leser kann sich vorstellen, dass Skripte von Assets, Anwendungsressourcen oder vom Server heruntergeladen werden können. Was kann zum Beispiel in Spielen nützlich sein? Glücklicherweise ist luaj mit einem der beliebtesten Java-Frameworks für Spiele kompatibel - Libgdx. Im Allgemeinen ist der Umfang hier nur durch die Vorstellungskraft des Entwicklers begrenzt.

Beispielquellen
Luaj
Jython
Jruby
Rhino ( Android Wrapper )

Source: https://habr.com/ru/post/de452236/


All Articles