Navigating through runtime only modules and thinking outside of the box with androidx.startup and koin (decoupled multi-module projects)

androix.startup is a new addition to the Jetpack suite of libraries. Read more about it here

Koin is a pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection!

Introduction

Problem Statement

  • Accessing components from your feature modules (dynamic or runtime only)
  • Initialization of sub-components of your modules without explicitly managing this yourself

Sample Structure

Image for post
Image for post

Let’s set up a few case scenarios which we need solutions to, at the very least we want to be able to get a static fragment class definition and a bunch of intents to launch activities or services .e.t.c

We’ll only focus on the following modules:

  • discover-movie (feature module that is only available at run time)
  • discover-series (feature module that is only available at run time)
  • navigation (module for handling how to navigate between modules)

Reflection

While reflection is powerful it does have some drawbacks and limitations which you can read more about at the source above and here

Example:

Image for post
Image for post

Now to define our components targets (full code extensions code can be found here):

Image for post
Image for post

Each of our target have to know the fully qualified package name of the component we need to access, as you can imagine if we change class names, or minify then we might end up mismatching class package names.

Accessing our components:

Image for post
Image for post
Model to hold fragment definition and params
Image for post
Image for post
Accessing navigation targets

In the sample above we’re calling NavigationTargets and getting specific components specifically in this case only fragments in the form of Class<out Fragment> because I’m using the default fragment factory as the constructors for each of the feature fragments are empty

Service Loader

Typically using a service loader in android as is introduces some performance penalties unless you use R8 shrinker to optimize/rewrite the implementation to avoid the performance penalties. Since service loader is no longer supported by R8 for dynamic feature modules we won’t be covering this topic

Dependency Injection

Example:

Image for post
Image for post
Notice that we’re not defining more than we need in this contract, this is because we’d rather define what we need and extend this interface if we need to access more modules. Full code

Define navigation targets that we’ll use in various parts of the system:

Image for post
Image for post
Contract for navigation targets, we don’t really need to define invoke at this point, it could be an extension function or whatever you want to use. Full code
Image for post
Image for post
Take not that each of the defined navigation targets here extend the INavigationFeature. Full code
Image for post
Image for post
An extension that we’ll showcase later to get fragment definitions

Let’s hook up the Provider for each of our navigation targets in the appropriate feature module and register them in our dependency injector.

Image for post
Image for post
discover-show feature implements contract from navigation module and provides details of implementation. Full code

Let’s continue to define our DI modules

Image for post
Image for post
Note how we’re setting our featureModule is set to provide a type of NavShow.Provider. Full code

DynamicFeatureModuleHelper is just a helper class that implements the detail of how how the koin should load or unload modules

Registering the specific module to the application global dependency injection registry, since this is a run time only module we can leverage androidx.startup to load this features modules dependencies.

Image for post
Image for post
Feature initializer to initialize our declared DI modules 💁 Full code
Image for post
Image for post
Manifest for FeatureInitializer, this exists in our feature module manifest. Full code

Finally accessing a component from anywhere within the application 😋

Image for post
Image for post
Full code

Conclusion

I believe there’s nothing new about any of the approaches demonstrated here, but simply an alternative look approach specifically tailored to Koin in the DI example.

The concept should be the same regardless of whichever DI engine you use, but obviously constraints may exist with your selected library of choice which will influence your design. I hope this was informative and will help you in future, feel free to leave some suggestions, thoughts or criticism 😃

Extras

Written by

Freelancer and open source developer, just trying to improve my skills one byte a time :)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store