Site icon AppTractor

Сравнение Koin и Dagger Hilt в современной Android-разработке

При выборе фреймворка для инъекции зависимостей для разработки на Kotlin для Android производительность часто является ключевым фактором. В этой статье рассматривается производительность Koin в его последней версии (4.0.1-Beta1) и проводится сравнение с Dagger Hilt (2.52). Вместо того чтобы полагаться на упрощенные бенчмарки или ограниченные сценарии выполнения кода, мы ориентируемся на разработчиков: понимание производительности в реальном мире, при повседневном использовании. Кроме того, эта статья призвана успокоить тех, кто, возможно, колеблется в выборе Koin из-за проблем с производительностью.

Что сравнивать?

Бенчмаркинг таких фреймворков ставит перед нами серьезную задачу: обеспечить справедливое сравнение и сосредоточиться на эквивалентном поведении и возможностях.

Чтобы сделать это упражнение значимым, я выбрал подход, ориентированный на пользователя: оценка времени, которое требуется для создания компонента, запрашиваемого из пользовательского интерфейса (например, ViewModel и так далее…). Чтобы убедиться, что наш тестовый контекст достаточно силен, нам нужно достаточно сложное приложение (не базовое приложение «Hello World» или «Список дел»).

Для этой цели я решил использовать приложение Google Now in Android — отличное приложение с открытым исходным кодом, которое достаточно сложное и охватывает проблемы реальной разработки, а команда Android демонстрирует лучшие практики (модульность, Jetpack Compose, внедрение зависимостей…).

Оценивая Koin и Dagger Hilt в этой среде, мы стремимся получить сведения, которые действительно важны для разработчиков Android.

Исходники доступны тут: https://github.com/InsertKoinIO/nowinandroid

там ыы найдете следующие ветки:

И не забудьте про официальную документацию по Koin. Итак, давайте погрузимся в детали!

Service Locator или Dependency Injection? Koin может и то, и другое!

Прежде чем погрузиться в бенчмарки, давайте разберемся с распространенным вопросом о Koin: является ли он локатором служб или фреймворком инъекции зависимостей? Ответ — и то, и другое.

Koin соединяет эти два подхода, предлагая динамический поиск через get() или inject() и одновременно поддерживая такие возможности DI, как инъекция конструктора и скопинг.

Динамическое поведение Koin зависит от жизненного цикла Android, что исторически делало внедрение конструкторов сложной задачей. Хотя современные функции Android теперь поддерживают инъекцию конструктора, Koin остается гибким, позволяя разработчикам выбирать наилучший подход для своих нужд.

По своей сути Koin — это DI-фреймворк. Он избегает накладных расходов на отражение, использует Kotlin DSL для графов зависимостей и поддерживает масштабируемые жизненные циклы. Однако его способность функционировать как локатор служб добавляет универсальности, особенно для более простых или унаследованных проектов.

Это краткое описание, но на этой странице документации по проекту Koin есть больше деталей, если вам нужно углубиться в подробности.

Почему стоит выбрать Koin?

Если вам интересно узнать о внутреннем устройстве и дизайне Koin, дайте мне знать — я с удовольствием расскажу об этом в одной из следующих статей. А пока давайте погрузимся в бенчмарки!

Отслеживание производительности

Отслеживание производительности компонентов в течение сессий сложнее, чем кажется на первый взгляд. Хотя такие инструменты, как Baseline Profiles Macrobenchmark и аналогичные инструменты глубокого погружения, предлагают отличный анализ, они не позволяют мне легко извлекать тестовые  значения в кастомном использовании. В качестве альтернативы можно использовать подключенные платформы для разработчиков, такие как Firebase Crashlytics или Kotzilla Platform, которые предлагают удобные решения для сбора и анализа показателей производительности.

Моя цель — оставаться простым и легким: я хочу измерить, сколько времени уходит на создание определенного компонента, например, на создание экземпляра ViewModel с помощью инъекции зависимостей. Мне не нужен сложный фреймворк для этой задачи, но я не против ручного инструментария, если он будет простым и легким.

Для этого я написал несколько функций для перехвата времени вызова функций из DI-фреймворков (все находится в файле Measure.kt). Эта утилита использует функцию Kotlin measureTimedValue — элегантный и эффективный способ измерения времени выполнения кода, поэтому она отлично подходит для легкого ручного инструментария. Расширив Android Context, я создал простой способ регистрации длительности любого вызова функции (или операции инъекции зависимостей) непосредственно в лог-файл.

inline fun <reified T> Context.measureTimeLazy(tag : String, code : () -> Lazy<T>) : Lazy<T>{
    val result = measureTimedValue(code)
    val timeInMs = result.duration.inWholeMicroseconds / 1000.0
    logBenchmark(tag,timeInMs)
    return result.value
}

В итоге мы сохраняем все результаты в локальном файле (функция logBenchmark). Этот файл будет извлечен, чтобы можно было рассчитать среднее время.

Now in Android

Теперь давайте посмотрим, как эти функции отслеживания применяются в нашем реальном сценарии. Для этого бенчмарка мы измерим производительность следующих компонентов: MainActivityViewModel, ForYouViewModel и время запуска.

Эти ViewModel являются первыми двумя, используемыми в приложении, что делает их идеальными кандидатами для оценки производительности DI-фреймворков на этапе начальной загрузки приложения.

В реализации Koin отслеживание производительности этих компонентов осуществляется следующим образом:

MainActivity

ForYouScreen

Примечание: мы используем последнюю функцию Koin AndroidX Startup для улучшения времени запуска.

Для реализации Hilt отслеживание применяется аналогичным образом:

MainActivity

ForYouScreen

Чтобы засечь время запуска, мы используем функцию onWindowFocusChanged в MainActivity. Она измеряет время, необходимое приложению для отрисовки первого кадра после получения фокуса, что дает четкое представление о производительности запуска приложения. Мы отслеживаем время от класса Application до первой активити:

MainActivity

Выполнение, извлечение и результаты

Для автоматического сбора показателей производительности мы запустили сценарий оболочки benchmark.sh. Этот скрипт автоматизирует последовательность действий по установке, запуску, ожиданию в течение нескольких секунд и остановке приложений для имитации реалистичных моделей использования. После выполнения всех действий он извлекает файл benchmark_log.txt, содержащий все записанные времена. Это 25 итераций запуска, ожидания и остановки приложения (демонстрационная сборка).

Используя собранные данные, Python-скрипт stats.py обрабатывает лог, чтобы вычислить ключевую статистику: минимальное, максимальное и среднее время для каждого компонента бенчмарка.

В терминале можно просто выполнить команду: benchmark.sh ; python3 stats.py (из папки /app).

Лучше всего запустить его на реальном устройстве Android. На моем OnePlus Nord (Android 12) я получил следующие результаты:

Результаты OnePlus Nord, а также в электронной таблице Google

Те же результаты OnePlus Nord в таблице

В этом тесте, помимо среднего, минимума и максимума, мы показываем «стандартную ошибку»: она измеряет надежность среднего, показывая, насколько сильно оно может отличаться от истинного среднего значения популяции. Меньшие значения означают более стабильные и точные результаты. Это также помогает сравнить результаты стабильности между Koin и Dagger Hilt.

Бенчмарки подчеркивают, что Koin — это надежная и современная альтернатива для разработки под Android, которая не уступает Hilt по производительности и при этом обладает собственными уникальными преимуществами.

Тем не менее, бенчмарки — это лишь часть истории. Результаты могут отличаться в зависимости от вашего приложения, но тенденции очевидны: Koin эффективен для решения реальных задач. От Android до Kotlin Multiplatform и Compose Multiplatform приложений.

Я всегда открыт для обратной связи — если у вас есть мысли или идеи, давайте пообщаемся!

Почему бы не попробовать Koin? Пусть Koin станет частью вашего путешествия!

Источник

Exit mobile version