Информация

Дата основания
Местоположение
Кипр
Сайт
fun.co
Численность
51–100 человек
Дата регистрации

Блог на Хабре

Обновить
185,26
Рейтинг
FunCorp
Разработка развлекательных сервисов

Гармония скриптов внутри Android приложения

Блог компании FunCorpРазработка мобильных приложенийРазработка под AndroidLuaKotlin
Tutorial


Думаю, многие читатели хаба по android-разработке слышали, что Java позволяет в рантайме через ClassLoader модифицировать dex уже установленного приложения. С помощью этого можно в рантайме подгружать скомпилированный код и использовать его. Но Google к таким махинациям относится, мягко говоря, не слишком лояльно и банит уличённые в подобном приложения.

Однако есть альтернативные способы загрузки и выполнения скриптов на мобильном устройстве. За подробностями под кат!

Итак, хотя мы не можем обновлять dex приложения в рантайме, мы можем воспользоваться интерпретаторами скриптовых языков, которые написаны целиком на Java. Так Oracle, начиная с 6 версии, включает javascript движок Rhino в состав JVM. Произошло это благодаря реализации спецификации JSR-223, которая декларирует поддержку в Java скриптовых языков программирования.

На текущий момент существует несколько встраиваемых движков для таких популярных языков программирования как: Lua (Luaj), Python (Jython), Ruby (Jruby) и java-script (Rhino, ...). Каждый из них позволяет как выполнять скрипты, так и обращаться к функциям, написанным на Java.
В качестве демонстрации возможностей я предлагаю реализовать “среду” разработки. Ссылку на исходники оставлю в конце статьи. Чтобы не загромождать пример, остановлюсь на Lua, хотя, ничто не мешает подключить все движки одновременно и переключаться между ними. Актуальная на момент написания статьи версия JLua доступная в mvnrepository: org.luaj:luaj-jse:3.0.1.
Каждая уважающая себя среда разработки должна иметь поле для ввода скрипта, поле для отображения результата и кнопочку, позволяющую выполнить своё детище.

UI уважающей себя среды разработки:

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

Для того, чтобы выполнить Lua-скрипт, нам надо получить глобальное окружение, в котором он будет выполняться, — Globals. Luaj позволяет настраивать его, например, устанавливая переменные или добавляя биндинги на Java-классы. Важной возможностью для нас тут будет задание потоков вывода сообщений, потому что по-умолчанию используется java.lang.System.out, java.lang.System.err, что не совсем удобно, когда нужно вывести результат выполнения в TextView. Чтобы это изменить нужно переопределить значения Globals#STDOUT и Globals#STDERR.

Таким образом, теперь нам остаётся только загрузить наш скрип в окружение и выполнить его.

Так это выглядит в моем примере:

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

Теперь попробуем расширить набор доступных функций возможностью показать Toast, используя упомянутую привязку Java классов. Сделать это легко, используя CoerceJavaToLua:

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

Результат у меня получился такой:



Таким образом, на небольшом примере мы рассмотрели возможность выполнения скриптов внутри мобильного приложения. Пытливый читатель может догадаться, что скрипты можно загружать из ассетов, ресурсов приложения или с сервера. Что может быть полезным, например, в играх. Благо, luaj совместима с одним из самых популярных игровых java фреймворков — Libgdx. В целом, сфера применения тут ограничивается только фантазией разработчика.

Исходники примера
Luaj
Jython
Jruby
Rhino (android wrapper)
Теги:android developmentluakotlinmobile development
Хабы: Блог компании FunCorp Разработка мобильных приложений Разработка под Android Lua Kotlin
Рейтинг +33
Количество просмотров 6,4k Добавить в закладки 47
Комментарии
Комментарии 2

Похожие публикации

Лучшие публикации за сутки