Здравствуйте, меня зовут Кристина Брагина, сегодня мы с Кристиной Фатеевой расскажем про нашу работу на тему “Особенности мультплатформенной разработки” в рамках проекта на бирже самгту “Разработка интеллектуальной платформы для изучения естественных языков”
Разработка приложения сегодня требует больших вложений человеко-часов, всегда над проектом трудится несколько команд, где каждая команда отвечает за одну определенную платформу. Зачастую разработчики в разных командах создают одинаковые интерфейсы, одинаковую бизнес логику, но с использованием совершенно разных программных средств и языков. Кроссплатформенные инструменты позволяют сократить трудозатраты и ускорить выпуск приложений в том случае, если требуется поддержка нескольких платформ одновременно. В долгосрочной перспективе кроссплатформенные решения помогут упростить и удешевить развитие программного продукта.
(2 слайд)
Главный принцип, лежащий в основе кроссплатформенных приложений - разделение кода на 2 части:
- кроссплатформенную, живущую в виртуальном окружении и имеющую ограниченный доступ к возможностям целевой платформы;
- нативную, которая обеспечивает инициализацию приложения, управление жизненным циклом ключевых объектов и имеющую полный доступ к системным API.
Общая архитектура кроссплатформенных приложений показана на рисунке. Для связи между “нативной” и “кроссплатформенной” частями, необходимо использовать специальный мост (bridge), который и определяет возможности инструментов.
Все операционные системы имеют те или иные технические возможности по запуску кроссплатформенных приложений. Самое простое с технической точки зрения - использование WebView, которое есть у всех ОС. Вторым вариантом является использование механизмов низкого уровня (например, OpenGL ES и языка C/C++), это позволит разделять между проектами большинство логики (в играх). Если же необходим полный доступ к возможностям целевых операционных систем (ОС), то здесь начинают задействоваться системные API верхнего уровня - такой подход реализуется только в ReactNative. Чтобы лучше понять возможности и ограничения каждого из фреймворков, давайте рассмотрим, как архитектурно они устроены и какие из этого следуют возможности и ограничения.
(3 слайд) ReactNative дает возможность использовать скриптовый язык программирования JavaScript для описания интерфейса пользователя и логики работы приложений. Сам по себе код на JavaScript обеспечивает производительность, сопоставимую с браузером, так как под капотом у этой технологии браузерный движок Chromium. Однако и в архитектуре ReactNative присутствует мост, снижающий скорость работы с платформенной функциональностью и UI.
(4 слайд) Но совсем недавно на рынке появился новый игрок - Kotlin Multiplatform, который находится только в начале своего пути и имеет большие шансы обогнать конкурентов. его принципиально новый формат работы представлен на схеме
Итак, чем новое решение отличается от уже существующих?
(5 слайд)
-
Близость к нативному коду: Kotlin компилируется в байт-код для машин JVM, и в нативный код для iOS и Web. Это обеспечивает производительность сильно выше и от того дает меньшую нагрузку на устройство в отличие Flutter и React Native. (Flutter из-за особенностей своей реализации (свой движок) сильно медленее чем React Native, а тот по своей сути является обычным браузером с JS интерпритатером. )
-
Нативная интеграция с платформами: Нативный подход позволяет более точечно решать вопросы глубокой интеграции с платформами. В случае с Flutter и React Native, несмотря на их способность интеграции с нативными элементами, не существует средств поддержки полноценного API операционной системы, например, не выйдет получить значение датчика яркости в смартфоне.
-
Популярность: Kotlin активно используется в Android разработке, и для разработчиков, переход на Kotlin Multiplatform не займет много времени. Flutter требует изучения языка Dart, который менее популярен среди разработчиков, а React Native требует хорошего понимания JavaScript и реактивного программирования.
-
Максимальное переиспользование кода: Kotlin Multiplatform максимизирует переиспользование бизнес-логики, позволяя разрабатывать UI отдельно для каждой платформы, что может дать бОльший контроль. Flutter и React Native, предоставляя свои средства для разработки UI, могут в некоторых случаях приводить к компромиссам в производительности и внешнем виде.
Из-за всех этих преимуществ в рамках нашего проекта на бирже было выбрано именно это средство разработки. После начала работ сразу же столкнулись с проблемами на нескольких операционных системах:
- Запись голоса отличается на всех платформах, необходимо для каждой писать собственную реализацию
- Нет средств работы с геолокацией на платформах
- Работа с уведомлениями уникальна для каждой системы
- Работа с данными
(6 слайд) Для решения платформенно-специфичных задач в Kotlin Multiplatform проекте обычно используется подход “expect/actual”. В данном подходе определяется “expect” декларация в общем коде (shared code), которая описывает интерфейс или функцию, которую нужно иметь на каждой платформе. Затем для каждой платформы предоставляется “actual” реализация, которая соответствует этим ожиданиям. Таким образом, получается, что необходимо реализовать 4 платфоменных реализации кода.
- Запись голоса отличается на всех платформах:
Чтобы реализовать запись звука, был определен общий интерфейс в shared code с функциями, такими как
startRecording(),stopRecording(). Затем в каждом специфичном модуле платформы создана актуальная реализация этого интерфейса, используя нативные API для записи звука.
(7 слайд) 2. Нет средств работы с геолокацией: Похожим образом, создан общий интерфейс для работы с геолокацией, включающий методы получения текущего местоположения пользователя. Актуальные реализации будут использовать Android Location API и Core Location framework в iOS, для Desktop максимум что мы можем использовать - это название сетей WiFi и IP адрес для получения примерного местоположения устройства.
(8 слайд) 3. Работа с уведомлениями: Работа с push-уведомлениями также будет использовать expect/actual, чтобы обрабатывать уведомления независимо от платформы. Определены общие функции для показа уведомлений пользователям и следующая структура данных, а затем использованы нативные механизмы для их реализации.
В андройде платформенное решение оформлено на NotificationCompat API из Android Jetpack, а на iOS используется UNUserNotificationCenter для отправки уведомлений:
Для отправки уведомлений на десктопных системах, таких как Windows, macOS и Linux используется библиотека NotificationsKt
(9 слайд) 4. Сохранение и работа с данными: Для работы с данными также используем кроссплатформенную библиотеку SQLDelight, которая уже поддерживает все платформы, мы всего лишь подключили ее к себе в проект. Таким образом, для решения наших проблем было написано несколько простых модулей.
10 слайд Внутренний проект В процессе разработки нашего приложения, заметили, что все наши наработки можно вынести в отдельный общий API за пределы нашего приложения. Таким образом появился проект Hayfork (c английского - вилы), его цель решить описанные ранее проблемы и упростить работу с платформенно-специфичным кодом. архитектуру решения можете увидеть на схеме. Весь проект делится на пять модулей, основной и четыре платформенных.
11 слайд Итог В работе были рассмотрены особенности кроссплатформенных фреймворков с точки зрения архитектуры. Сами по себе кроссплатформенные приложения сопоставимы с нативными, однако необходимость использования моста снижает скорость при взаимодействии с системными API, если не происходит компиляции сразу в нативный код, как в случае с Kotlin Multiplatform. В итоге, для реализации проекта была выбрана именно эта технология, в рамках требований проекта реализованы платформенно-специфичные утилиты и вынесены в отдельный API, доступный в общем модуле.