Create a separate dependency injection container for your libraries and SDKs
Dependency Injection is an essential aspect of modern Android development, and Koin has earned its place as one of the most developer-friendly libraries for this purpose. I’ve previously shared my thoughts on Koin in this article, which you can check out if you’re looking for an introduction.
Among Koin’s many features, Isolated Context stands out as a powerful tool for creating independent dependency graphs. It is perfect in scenarios like building modular libraries or SDKs, where a completely separate DI setup is crucial.
In this article, I’ll delve into what Isolated Context is, how it works, and how it can be applied and tested to design fully independent modules.
What is Koin’s Isolated Context?
Koin’s Isolated Context allows you to define a completely independent DI graph that operates separately from your main application’s modules configuration. This feature is invaluable when you need temporary or isolated setups without interfering with the primary DI environment.
It plays a crucial role in supporting modular applications, enabling you to run isolated modules within specific contexts.
For example, imagine you’re building a library that uses Koin internally for its dependency management. If the main application also uses Koin, you face two options:
- The dirty way: Document your library’s modules and instruct developers to include them in the main app’s Koin initialization.
- The better way: Use Context Isolation to create a completely independent instance of Koin, managing all objects for your library without affecting the main application’s setup.
By choosing the second approach, you ensure clean separation, improved modularity, and reduced risk of conflicts between dependency graphs.
Setting Up Koin’s Isolated Context
To get started, let’s assume your library already includes a Koin instance (if you’re unfamiliar with setting up Koin, I recommend checking out my previous article for a quick introduction).
The first step is to create an object class that will maintain your isolated context instance. This class should be a singleton, ensuring it remains accessible throughout the entire lifecycle of your library.
Here’s how you can define it:
object MyIsolatedKoinContext { val koinApp = koinApplication { modules(myModule) } val koin = koinApp.koin }
In this setup:
- koinApp is the Koin application instance that initializes with the provided modules.
- koin is the Koin instance, which will be used to resolve dependencies within your library.
You can define the modules (myModule) just as you normally would in Koin. There’s no special syntax or additional requirements for isolated contexts, they work seamlessly with the familiar Koin module definitions.
Using the Isolated Koin Context in Compose
Let’s see how to integrate MyIsolatedKoinContext into a Jetpack Compose environment.
The process is straightforward. Instead of relying on KoinAndroidContext, you can directly inject your isolated Koin context into the Compose hierarchy after calling setContent. Here’s how it looks:
setContent { KoinIsolatedContext(context = MyIsolatedKoinContext.koinApp) { // Your Compose content here } }
By doing this, all your Compose content will automatically use the isolated Koin context defined in MyIsolatedKoinContext.
This approach is not only simple but also powerful. With just one line of code, you gain the flexibility to switch Koin contexts without altering any of your existing Compose functions. It keeps your implementation clean and ensures the modularity of your app or library.
Using the Isolated Koin Context in Other Classes
Now that we’ve defined MyIsolatedKoinContext and used it in the Compose environment, let’s address how to handle other classes that rely on Koin.
For instance, you might already have something like this:
class Example : KoinComponent { // Existing Koin usage }
To ensure these classes use the isolated context instead of the default one, we’ll create our own implementation of KoinComponent that leverages the isolated Koin instance. This is simple to achieve with an abstract class:
abstract class IsolatedKoinComponent : KoinComponent { // Override the default Koin instance override fun getKoin(): Koin = MyIsolatedKoinContext.koin }
This abstract class overrides the getKoin method to return the isolated context from MyIsolatedKoinContext. Now, you can replace all occurrences of KoinComponent in your code with IsolatedKoinComponent.
Your updated code would look like this:
class Example : IsolatedKoinComponent { // Uses the isolated Koin context }
That’s it! Once again, this highlights the simplicity of Koin. With a small change in your class definition, you seamlessly switch to the new isolated context without needing to modify the actual logic inside your classes.
Job Offers
Testing
Testing with Koin is straightforward, as I discussed in my previous article. However, when dealing with complex cases , such as testing classes that use inject directly in their implementation, you can override the getKoin function in your test class to use the isolated context.
Here’s an example:
class MyClassTest : KoinTest { // Override the default Koin instance with the isolated context override fun getKoin(): Koin = MyIsolatedKoinContext.koin @Before fun setUp() { val testModule = module { // Define your test-specific dependencies here } koin.loadModules(listOf(testModule)) } }
With this approach, you can easily define custom modules for your tests, including mock implementations of your dependencies. This setup ensures that your tests are isolated and do not interfere with the main application’s DI graph.
Conclusion
Koin’s Isolated Context is a game-changer for modular and independent development in Android. It empowers developers to create standalone dependency injection setups for libraries and SDKs, ensuring clean separation and minimal interference with the main application’s DI configuration.
Throughout this article, we’ve explored how to set up and use an isolated context effectively whether in Compose, other classes, or even in tests. By adopting this approach, you can achieve a more modular architecture, simplify the integration process, and provide a better experience for both developers and users of your libraries or SDKs.
Koin continues to demonstrate its developer-friendly nature with features like Isolated Context, allowing for flexible and robust DI solutions. As you implement this in your projects, you’ll likely find it an invaluable tool in your Android development toolkit.
If you found this article interesting, feel free to follow me for more insightful content on Android development and Jetpack Compose. I regularly publish new articles on these topics. Don’t hesitate to share your comments or reach out to me on Bluesky or LinkedIn for further discussions.
Have a great day, and happy coding!
This article is previously published on proandroiddev.com.