
Acho que muitos leitores do hub de desenvolvimento do Android ouviram dizer que o Java permite modificar o dex de um aplicativo já instalado em
tempo de execução via
ClassLoader . Usando isso, você pode carregar o código compilado em tempo de execução e usá-lo. Mas o Google trata essas fraudes, para dizer o mínimo, não com muita lealdade e proíbe os que são pegos em tal aplicativo.
No entanto, existem maneiras alternativas de baixar e executar scripts em um dispositivo móvel. Para detalhes sob o gato!
Portanto, embora não possamos atualizar aplicativos dex em tempo de execução, podemos usar intérpretes de linguagens de script escritas inteiramente em Java. Portanto, o Oracle, começando com a versão 6, inclui o mecanismo jhascript Rhino na JVM. Isso aconteceu graças à implementação da especificação JSR-223, que declara suporte para linguagens de programação de script em Java.
Atualmente, existem vários mecanismos internos para linguagens de programação populares como: Lua (Luaj), Python (Jython), Ruby (Jruby) e java-script (Rhino, ...). Cada um deles permite que você execute scripts e acesse funções escritas em Java.
Como demonstração das possibilidades, proponho implementar um "ambiente" de desenvolvimento. Vou deixar um link para as fontes no final do artigo. Para não confundir o exemplo, vou me concentrar em Lua, embora não exista nada para impedir que você conecte todos os mecanismos ao mesmo tempo e alterne entre eles. A versão atual do JLua no momento da escrita está disponível no mvnrepository:
org.luaj: luaj-jse: 3.0.1 .
Cada ambiente de desenvolvimento que se preze deve ter um campo para inserir um script, um campo para exibir o resultado e um botão que permita concluir sua ideia.
Interface do usuário de um ambiente de desenvolvimento que se preze:
<?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 executar o script Lua, precisamos obter o ambiente global em que ele será executado -
Globals . Luaj permite personalizá-lo, por exemplo, definindo variáveis ou adicionando ligantes às classes Java. Uma oportunidade importante para nós aqui é definir fluxos de saída de mensagens, porque
java.lang.System.out ,
java.lang.System.err é usado por padrão, o que não é muito conveniente quando você precisa exibir o resultado da execução no TextView. Para alterar isso, é necessário redefinir os valores
Globals # STDOUT e
Globals # STDERR .
Assim, agora só precisamos carregar nosso rangido no ambiente e executá-lo.
É assim que parece no meu exemplo:
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() } }
Agora vamos tentar expandir o conjunto de funções disponíveis com a capacidade de mostrar o
Toast usando a ligação acima das classes Java. Facilite o uso do
CoerceJavaToLua :
globals.set("bubble", CoerceJavaToLua.coerce(Bubble(this))) ... private class Bubble(private val context: Context) {
O resultado que obtive é este:

Assim, com um pequeno exemplo, consideramos a possibilidade de executar scripts dentro de um aplicativo móvel. Um leitor curioso pode adivinhar que os scripts podem ser baixados de ativos, recursos de aplicativos ou do servidor. O que pode ser útil, por exemplo, em jogos. Felizmente, luaj é compatível com um dos mais populares frameworks de jogos em Java - Libgdx. Em geral, o escopo aqui é limitado apenas pela imaginação do desenvolvedor.
→
Fontes de amostra→
Luaj→
Jython→
Jruby→
Rhino (
android wrapper )