Разработка мобильных приложений — это всегда компромисс между тем, что хочется сделать и тем, что позволяет сделать платформа устройства. В этой статье рассказывается о том, как увеличить возможности приложения Android с помощью Golang.
Здесь вы не найдете утечки секретного api android. Используемые механизмы — это стандартные (или почти стандартные) инструменты android и golang, описание которых есть на официальных сайтах и профильных форумах. Но в виде единого плана действий, да еще и на русском языке — такое публикуется впервые и эксклюзивно для Хабра.
Предыстория
По многочисленным просьбам пользователей одного из приложений были разработаны предсказания текста. Предсказания обслуживал алгоритм radix tree, реализованный в отдельной android-библиотеке. Алгоритм показывал хорошую производительность наряду с экономным потреблением ресурсов — выдавал результат за десятки миллисекунд при потреблении 1,5 Мб памяти. Вполне рабочие показатели. Но так было только при изолированном тестировании библиотеки на JVM.
Проблемы начались при подключении библиотеки к приложению. На android алгоритм предсказаний стал тормозить, выдача результатов затянулась до 7-ми секунд. Ничего себе! Да за 7 секунд можно вручную набрать слово, стереть и набрать его правильно. Такие предсказания никуда не годились.
Проведенный анализ не выявил проблем кода. Алгоритм работал безупречно, памяти потреблял в строго отведенных количествах, не отвлекался на посторонние процессы, все асинхронно, аппаратных ресурсов предостаточно. Вывод напрашивался только один — виртуальная машина android dalvik имеет производительность, отличную от JVM, и в рамках dalvik данный код обречен на тормоза.
Тогда и было решено, что алгоритм предсказаний нужно выносить из dalvik. Для этого существует JNI — Java Native Interface. Механизм, позволяющий вызывать из java методы библиотек, написанных на C/C++. И обратно, — из библиотеки C/C++ вызывать методы java.
Решение
Для создания нативной библиотеки был выбран язык Go. Просто потому что имеется опыт работы с ним, в отличие от C/C++. Golang восхитителен, но этот путь имеет свои трудности, поскольку мы получаем дополнительный уровень сложности. Для чистого C/C++ достаточно использовать NDK и следовать инструкциям, описанным на сайте android developers. Для Golang придется изучить еще и компиляцию golang под android, начать можно здесь.
Впереди лежал путь, полный опасностей и приключений. Но сейчас этот путь уже пройден, и я с удовольствием поделюсь его планом.
План
Среда разработки android:
- ОС: Windows 7.
- IDE: Eclipse with ADT. Но этот план подойдет и для Android Studio.
Среда разработки golang:
- ОС: Для компиляции golang под android используется linux. Я использую Ubuntu 14.04, запущенную на VirtualBox.
- Android SDK.
— Cкачать архив с официального сайта android
— Распаковать, допустим, в
$HOME/android/android-sdk-linux
- Java JDK.
$ apt-get default-jdk
- Go 1.5.
Текущая версия релиза Go — 1.4. Но у меня не получилось скомпилировать библиотеку с его помощью, где-то ошибся при сборке toolchain. Поэтому использовал пакет gomobile из девелоперской версии Go 1.5, релиз которой еще только планируется. Пока нет релиза, установка Go 1.5 описана здесь:
— Установить Go 1.4 golang.org/doc/install
— Клонировать текущий репозиторий golang:
— Скомпилировать текущую версию Go с помощью Go 1.4$ git clone https://go.googlesource.com/go $HOME/go
$ export GOROOT_BOOTSTRAP=/usr/local/go $ cd $HOME/go/src && ./make.bash $ export PATH=$PATH:$HOME/go/bin
- Установить и инициализировать пакет gomobile.
Инициализация пакета занимает приличное время$ go get golang.org/x/mobile/cmd/gomobile $ gomobile init
Разработка библиотеки golang.
- Разрабатывается обычный golang package, никаких дополнительных рекомендаций. Экспортные методы будут доступны из android.
Компиляция библиотеки golang в библиотеку android *.aar.
- Использовать команду gomobile bind с указанием пути до android sdk
$ cd <golang project folder> $ ANDROID_HOME=$HOME/android/android-sdk-linux gomobile bind
Подключение библиотеки *.aar к приложению:
Прежде всего, передать библиотеку *.aar на машину разработки android. Далее в зависимости от IDE.
для Eclipse:
- Распаковать библиотеку *.aar во временный каталог
- В Eclipse создать новый проект из существующего кода, указав временный каталог из п.1
- Отметить, что данный проект является библиотекой android
- Подключить каталог jni и файл classes.jar в Build Path нового проекта
- Подключить новый проект как библиотеку в проект приложения
- Скопировать содержимое папки jni в папку libs проекта приложения.
- Скопировать содержимое proguard.txt библиотеки в proguard.txt проекта приложения (защита классов go* от обфускации)
- При последующих обновлениях библиотеки *.aar достаточно обновить файлы в папках jni, libs и файл classes.jar
для Android Studio:
- Подключить готовую библиотеку *.aar к проекту, используя стандартные средства Android Studio. К сожалению, у меня нет такого опыта, поэтому оставляю это в качестве домашнего задания.
Вызов методов нативной библиотеки из android.
- Перед использованием нативной библиотеки в приложении, нужно выполнить её инициализацию. Подходящее для этого место — в методе onCreate:
public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); Go.init(getContext()); } }
- Методы библиотеки golang доступны через package, одноименные с теми, что вы определили в golang. Например:
golang:
package radix … func Suggest(params string) string { … }
java:
suggestions = Radix.Suggest(params);
Результат
После переноса алгоритма в нативную библиотеку, предсказания стали работать даже быстрее, чем в JVM — результат отдается менее чем за 10 мс. Также уменьшилось количество garbage collection, потому что часть ресурсов была передана на сторону нативного кода.
Эффект от внедрения JNI превзошел ожидания. С такими показателями клавиатура и отправилась в релиз.
Заключение
Ускорить приложение android с помощью golang — это вовсе не теория, а реальная возможность, которая уже используется в приложениях маркета.
Конечно, это усложняет разработку приложений, зоопарк инструментов удваивается. Но это даёт возможность реализовать интересные решения.
С выходом Go 1.5 ожидается, что интеграция android-golang станет еще доступнее, за счет инструментария gomobile, который сокращает весь процесс до 2-х команд.