Rust — это язык системного программирования общего назначения, который существует уже довольно давно. Его можно использовать для выполнения задач, которые реализуются сейчас на C и C++, но с гораздо большей безопасностью памяти. Это позволяет использовать Rust для написания программ или скриптов для многих операционных систем, включая Android. Вы можете задаться вопросом, как это возможно и есть ли простой способ сделать это. Вот об этом эта статья!
В настоящее время не так много информации о том, как писать код на Rust в Android-приложениях. Есть некоторая информация, предоставленная Google, но она сложна для понимания новичком. Цель этого пошагового руководства — предоставить простое, но эффективное руководство по интеграции кода Rust в разработку для Android. Никаких предварительных знаний C или C++ или JNI не требуется.
Настройка
Мы будем использовать Android Studio. Начнем с его настройки.
Плагин Rust
Во-первых, нам нужен плагин Rust. Откройте Android Studio. Откроется диалоговое окно, подобное показанному ниже. Выберите вкладку «Плагины», найдите «Rust», а затем установите официальный плагин Rust от JetBrains. В качестве альтернативы, если у вас уже открыт проект, нажмите «Файл» в верхнем левом углу Android Studio и выберите «Настройки». Нажмите «Плагины».
Создаем пустое приложение
Теперь давайте создадим новое пустое приложение. Начните со стандартной настройки нового проекта.
Назовем наш проект Rust Application и нажмем «Готово».
Появится окно, похожее на показанное ниже.
Включаем Cargo Project
Давайте теперь добавим проект Cargo (менеджер пакетов для Rust) следуя этому руководству. Чтобы внедрить его в проект Rust, просто щелкните Терминал в Android Studio и введите это:
C:\Users\USER1\AndroidStudioProjects\RustApplication>cargo new rust_lib --lib
Это создает новую библиотеку, чтобы ее можно было использовать из нашего приложения. Мы назвали библиотеку rust_lib. Обратите внимание, где создается папка.
Чтобы просмотреть папку в Android Studio, перейдите на панель Project и выберите Project в раскрывающемся меню вместо Android.
Вы увидите новую папку с именем rust_lib. Эта папка по умолчанию содержит новый репозиторий git, файл Cargo.toml и папку src, содержащую lib.rs. Теперь давайте изменим тип библиотеки, которую мы только что создали, используя файл Cargo.toml. Для разработки мобильных приложений библиотека должна быть динамической.
Добавьте следующее к содержимому файла Cargo.toml.
[lib] name = "rust_lib" crate-type = ["cdylib"]
Посмотреть суть здесь.
Добавление зависимостей
Давайте добавим зависимости, которые создадут необходимые файлы, чтобы сделать связывание нашего Rust кода с Android гладким процессом.
Используйте «*», чтобы получить последнюю версию зависимости.
Flapigen — это основная зависимость сборки для генерации соответствующего кода из нашего Rust кода и использования в Android-приложении. Flapigen работает с файлом интерфейса, но необходимость изменять файл интерфейса каждый раз при изменении кода может стать утомительной. Вот тут-то и появляется rifgen. Это упрощает создание файла интерфейса.
Дополнительные зависимости предназначены для ведения логов.
Создание файла сборки
Как упоминалось ранее, flapigen и rifgen являются зависимостями сборки и взаимодействуют с файлом build.rs. Щелкните правой кнопкой мыши папку rust_lib и выберите New > Rust File.
Назовите файл build.rs.
Следуя руководству, предоставленному flapigen и rifgen, наш файл build.rs должен выглядеть примерно так:
Вам может быть интересно, что происходит в файле build.rs. Flapigen преобразует то, что находится в файле интерфейса, в Rust файл java_glue. Поэтому мы сначала указываем исходный файл для файла интерфейса (in_src), а затем выходной файл (java_glue.rs). Каталог java_glue.rs должен совпадать с переменной среды OUT_DIR. После этого используется rifgen для генерации файла интерфейса, с нашими предпочтениями в зависимости от языка, к которому мы добавляем проект Rust. Последний параметр Generator::new указывает начало папки, содержащей наш Rust код, а функция generate_interface принимает путь к файлу интерфейса, то есть in_scr. Папка java_folder указывает, куда должны помещаться созданные java-файлы. Вызов swig_gen.expand указывает flapigen сгенерировать соответствующие файлы. Обратите внимание на эти параметры, поскольку мы используем их в сборке Gradle.
Наконец, создайте файл с именем java_glue.rs в папке rust_lib/src и поместите в него следующее содержимое:
И добавьте:
mod java_glue; pub use crate::java_glue::*;
в ваш файл lib.rs. Это соединит ваш Rust код со сгенерированным.
Добавляем Android Toolchain и линкеры
Чтобы скомпилировать Rust для Android, нам нужно добавить инструменты Android в rustup. Для этого просто запустите в Терминале следующее:
>rustup default nightly >rustup target add aarch64-linux-android armv7-linux-androideabi
Поскольку rifgen крейт работает с nightly, вам нужно сначала установить Rust nightly.
Затем вы добавляете наборы инструментов Rust и стандартную библиотеку для 64-битной и 32-битной версий Android соответственно. Теперь давайте добавим компоновщики компилятора. Его нужно добавить в файл rust_lib/.cargo/config.toml.
Щелкните правой кнопкой мыши папку rust_lib, создайте новый каталог с именем .cargo, затем создайте новый файл в каталоге .cargo с именем config.toml.
Соответствующие компоновщики поставляются с Android NDK, поэтому их следует загрузить, прежде чем продолжить.
Добавьте следующее в созданный файл конфигурации.
Замените ANDROID SDK на путь к Android SDK (или папку, содержащую пакет NDK). Кроме того, замените OS VERSION на вашу версию ОС. Например, на моем компьютере с Windows полный путь:
[target.aarch64-linux-android] linker = "C:\\Users\\taimoor\\AppData\\Local\\Android\\Sdk\\ndk-bundle\\toolchains\\llvm\\prebuilt\\windows-x86_64\\bin\\aarch64-linux-android21-clang++.cmd"
Обратите внимание на .cmd в конце сборки Windows.
Использование Gradle для автоматизации сборки
Gradle можно использовать для автоматического запуска сборки cargo всякий раз, когда мы хотим протестировать приложение. Это приведет к тому, что изменения, которые мы сделали на стороне Rust, будут автоматически обновлены в нашем приложении.
Обратите внимание, что это файл Gradle уровня приложения или модуля, а не файл Gradle уровня проекта. Добавьте строки с 20 по 27 и строку 65 и далее. Строка 65 и далее просто указывает Gradle запускать сборку cargo всякий раз, когда мы запускаем приложение. После запуска команды сборки cargo мы копируем созданные java-файлы и файл динамической библиотеки (.so) и вставляем их в каталог, который может быть прочитан Android и использован в нашем коде Kotlin.
Теперь приложение должно работать 😄.
Бонус: ведение логов
Давайте быстро реализуем ведение журнала, чтобы облегчить отладку нашего кода на Rust. В файл lib.rs добавьте следующее содержимое:
Мы используем крейт android_logger для создания журналов, а затем вызываем log_panics::init(), чтобы перенаправить все паники для регистрации, а не для вывода стандартной ошибки. Обратите внимание на атрибут #[generate_interface]. Это сообщает rifgen, что мы будем вызывать эту функцию из Kotlin, поэтому он должен добавить этот вызов метода в файл интерфейса.
Загрузка библиотеки из Kotlin
Теперь, когда мы настроили сторону Rust, давайте перейдем к стороне Kotlin. Теперь мы загрузим библиотеку из синглтона:
Добавьте различный импорт для созданного нами класса Logs. Обратите внимание, хотя мы и назвали функцию initialise_logging, мы можем использовать ее как initialiseLogging. Это потому, что мы указали CamelCase при настройке rifgen.
Запустите программу, чтобы увидеть информацию из логов кода Rust.