Blog Infos
Author
Published
Topics
, , , , ,
Published

When choosing a dependency injection framework for Android and Kotlin development, performance is often a key consideration. This article explores the performance of Koin in its latest version (4.0.1-Beta1) and compares it with Dagger Hilt (2.52). Rather than relying on simplistic benchmarks or limited code execution scenarios, the focus is “developer-centric”: understanding performance in real-world, day-to-day usage. Additionally, this article aims to reassure those who may hesitate to adopt Koin due to performance concerns.

From original Google’s Now in Android banner.

What to benchmark?

Benchmarking such frameworks poses a significant challenge: ensuring fair comparison and focused on equivalent behaviors and features.

To make this exercise meaningful, I’ve opted for a user-oriented approach: evaluating the time it takes to build a component requested from the UI (like ViewModels and so on …). To ensure our test context is strong enough, we need a complex enough application (no basic “Hello World” or to-do list app).

For this purpose, I’ve chosen to use Google’s Now in Android app, a great open-source application that is complex enough and covers the challenges of real-life development and where the Android team demonstrates best practices (modularization, Jetpack Compose, and dependency injection …).

By evaluating Koin and Dagger Hilt in this environment, we aim to get insights that truly matter to Android developers.

Sources are available at https://github.com/InsertKoinIO/nowinandroid

You will find the following branches:

  • perfs_koin —is the Now in Android migrated to Koin branch, with performances measurement
  • perfs_hilt— is the default Hilt branch, with performances measurement

And don’t forget official Koin documentation. Now, let’s dive into the details!

Service Locator or Dependency Injection? Koin can do both!

Before diving into the benchmarks, let’s address a common question about Koin: Is it a Service Locator or a Dependency Injection (DI) framework? The answer is both.

  • Service Locator retrieves dependencies dynamically through a centralized registry.
  • Dependency Injection provides dependencies explicitly at instantiation, enhancing testability and maintainability.

Koin bridges these two approaches, offering dynamic retrieval via get() or inject() while also supporting DI features like constructor injection and scoping.

Koin’s dynamic behavior is influenced by Android’s lifecycle, which historically made constructor injection challenging. While modern Android features now support constructor injection, Koin remains flexible, letting developers choose the best approach for their needs.

At its core, Koin is a DI framework. It avoids reflection overhead, uses a Kotlin DSL for dependency graphs, and supports scoped lifecycles. However, its ability to function as a Service Locator adds versatility, particularly for simpler or legacy projects.

This is a summary, but this Koin project documentation page has more details if you need to go deeper.

Why choose Koin?
  • Simple and Developer-Friendly: Koin’s clean DSL, no compile-time overhead, minimal setup, and easy testing let you focus on building your apps.
  • Scales with Your App: from small apps to complex projects, Koin scales effortlessly to meet your needs.
  • Evolving Compile-Time Safety: With features like module validation (Verify API), Koin Annotations (KSP for configuration safety), and the upcoming Koin IDE Plugin, Koin simplifies coding while boosting safety.
  • Ready for Kotlin Multiplatform: Koin seamlessly manages dependencies across iOS, Android, Desktop, and Web, making it the go-to DI framework for cross-platform development.
  • Perfect for Compose Multiplatform: Koin integrates effortlessly with Compose Multiplatform, supporting shared logic and DI for UI components — even ViewModel.

If you’re curious about Koin’s internals and design, let me know — I’d be happy to explore that in a future article. For now, let’s dive into the benchmarks!

Tracking Performances

Tracking the performance of components over sessions is trickier than it initially seems. While tools like Baseline Profiles Macrobenchmark and similar deep-dive tools offer great analysis, they don’t allow me to easily extract benchmark values for custom use. Alternatively, connected dev platforms like Firebase Crashlytics or Kotzilla Platform offer convenient solutions to capture and analyze performance metrics.

My goal here is to stay simple and lightweight: I want to measure how long it takes to create a specific component, like building a ViewModel instance using dependency injection. I don’t need a complex framework for this task, but I’m OK with manually instrumenting my code as long as it’s straightforward and lightweight.

To achieve this, I wrote a few functions to capture function call time from DI frameworks (All is in Measure.kt file). This utility leverages Kotlin’s measureTimedValue function, an elegant and efficient way to measure code execution times, making it an excellent fit for lightweight, manual instrumentation. By extending the Android Context, I created an easy way to log the duration of any function call (or dependency injection operation) directly to a log file.

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
}

 

In the end, we are storing all results in a local file (function logBenchmark). This file will be extracted, to allow average times calculation.

Now in Android

Now, let’s see how these tracking functions are applied in our real-world scenario. For this benchmark, we’ll measure the performance of the following components: MainActivityViewModelForYouViewModel, and startup time.

These ViewModels are the first two used in the application, making them ideal candidates for assessing the performance of DI frameworks during the app’s initial loading phase.

In the Koin implementation, the performance tracking for these components is instrumented as follows (MainActivity & ForYouScreen links):

ForYouScreen

A note here: we are using the latest Koin AndroidX Startup feature to help improve startup time.

For the Hilt implementation, tracking is similarly applied (MainActivity & ForYouScreen links):

ForYouScreen

To capture the startup time, we use the onWindowFocusChanged function in MainActivity. This measures the time it takes for the app to render its first frame after gaining focus, giving a clear picture of the app’s startup performance. We track time from the Application class until the first Activity:

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Let’s build a performance measuring tool from scratch

Did you know you can measure the performance of production apps? Android is based on Linux, so that gives us a lot of power!
Watch Video

Let's build a performance measuring tool from scratch

Alexandre Moureaux
Performance Expert
Theodo Apps

Let's build a performance measuring tool from scratch

Alexandre Moureaux
Performance Expert
Theodo Apps

Let's build a performance measuring tool from scratch

Alexandre Mourea ...
Performance Expert
Theodo Apps

Jobs

Execution, Extraction, And Results

To capture performance metrics automatically, we run the benchmark.sh shell script. This script automates a sequence of app install, start, wait a few seconds, and stop actions to simulate realistic usage patterns. After all runs, it extracts the benchmark_log.txt file containing all recorded times. This is 25 iterations of running the Nia application’s start, wait and stop (demo release build).

Using the collected data, the stats.py Python script processes the log to compute key statistics: minimum, maximum, and average times for each benchmarked component.

On your terminal, you can just run the command: benchmark.sh ; python3 stats.py (from the /app folder).

The best is to run it on a real Android device. On my OnePlus Nord (Android 12), I got the following results:

OnePlus Nord results, and also in Google spreadsheet

Same OnePlus Nord results in table

In this benchmark, in addition to average, minimum, and maximum, we show the “standard error”: it measures the reliability of the average, indicating how much it may vary from the true population mean. Smaller values mean more stable and precise results. It helps also compare stability results between Koin and Dagger Hilt.

The benchmarks highlight Koin as a reliable and modern alternative for Android development, matching Hilt in performance while offering its own unique advantages.

That said, benchmarks are just one part of the story. Your results may vary depending on your app, but the trends are clear: Koin is performant for real-world challenges. From Android to Kotlin Multiplatform and Compose Multiplatform applications.

I’m always open to feedback — if you have thoughts or insights, let’s chat!

Why not give Koin a shot? Let Koin be part of your journey!

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
This is the second article in an article series that will discuss the dependency…
READ MORE
blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
blog
One of the latest trends in UI design is blurring the background content behind the foreground elements. This creates a sense of depth, transparency, and focus,…
READ MORE
blog
Now that Android Studio Iguana is out and stable, I wanted to write about…
READ MORE
Menu