• Latest
Our Experience Migrating From Dagger to Koin

Our Experience Migrating From Dagger to Koin

December 11, 2021
Fireworks photos made easy: Getting the most bang out of your images

Fireworks photos made easy: Getting the most bang out of your images

July 1, 2022
Nintendo Launches Subscription Service For Switch Repairs In Japan

Nintendo Launches Subscription Service For Switch Repairs In Japan

July 1, 2022
Kao the Kangaroo roadmap promises achievement fixes

Kao the Kangaroo roadmap promises achievement fixes

July 1, 2022
OnePlus 7 and 7T are finally getting Android 12

OnePlus 7 and 7T are finally getting Android 12

July 1, 2022
Xbox Cloud Gaming launches on Samsung smart TVs

Xbox Cloud Gaming launches on Samsung smart TVs

July 1, 2022
FIST: Forged In Shadow Torch Gets Physical Edition In September

FIST: Forged In Shadow Torch Gets Physical Edition In September

July 1, 2022
Google settles lawsuit with US app developers for $90 million

Google settles lawsuit with US app developers for $90 million

July 1, 2022
OnePlus Nord 2T debuts in India, sales begin July 5

OnePlus Nord 2T debuts in India, sales begin July 5

July 1, 2022
Fire Emblem Warriors: Three Hopes Was Originally Fire Emblem Warriors 2

Fire Emblem Warriors: Three Hopes Was Originally Fire Emblem Warriors 2

July 1, 2022
OnePlus Nord 2T 5G hands-on review

OnePlus Nord 2T 5G hands-on review

July 1, 2022
Apple now selling refurbished Mac Studio models, priced at a 10% discount compared to buying new

Apple now selling refurbished Mac Studio models, priced at a 10% discount compared to buying new

July 1, 2022
Apple launches Apple TV promotion: get a $50 gift card with Apple TV 4K or Apple TV HD purchase

Apple launches Apple TV promotion: get a $50 gift card with Apple TV 4K or Apple TV HD purchase

July 1, 2022
Advertise with us
Friday, July 1, 2022
Bookmarks
  • Login
  • Register
GetUpdated
  • Home
  • Game Updates
    • Mobile Gaming
    • Playstation News
    • Xbox News
    • Switch News
    • MMORPG
    • Game News
    • IGN
    • Retro Gaming
  • Tech News
    • Apple Updates
    • Jailbreak News
    • Mobile News
  • Software Development
  • Photography
  • Contact
    • Advertise With Us
    • About
No Result
View All Result
GetUpdated
No Result
View All Result
GetUpdated
No Result
View All Result
ADVERTISEMENT

Our Experience Migrating From Dagger to Koin

December 11, 2021
in Software Development
Reading Time:6 mins read
0 0
0
Share on FacebookShare on WhatsAppShare on Twitter


Note: This was assembled with Koin version 2.0.1. More recent versions have changed some things. Refer to the official documentation for more information.

Context

Our team has a legacy project, started by a team from another company, with other standards, practices, experiences, and so on. This project was initially set up with Dagger as a dependency injection mechanism and is not modularised. As the project grew, so did the compilation times. When it got to the point where compiling the project could take more than ten minutes, we decided to see what we could do about it.

Modularization: A Possible Solution?

We first considered modularizing the project, so that only the modified modules would have to be recompiled instead of the whole project. This would not solve the initial compilation time, but the incremental builds would be much faster.

But given the length of time the project had been under development without following good guidelines to reduce coupling, trying to get modules out was tremendously complicated.

Being able to modularise the project required a refactor at a very deep level, decoupling essential parts of the application from each other. We had to do all this while still delivering new functionality to the customer.

Dagger and Annotation Processing

Thanks to Android Studio’s build analysis tool, we were able to see that approximately 40 to 50 percent of the time in each build was taken up by the annotation processor — and practically all of that time was taken up by Dagger.

We had already worked on other projects using Koin, and given that the project code was already more than 90 percent Kotlin, we thought it was a good idea to migrate from one library to the other to see what would happen. In the worst-case scenario, we would end up with a dependency injection library that we already knew and were comfortable with.

Initial Configuration

We started the migration bit by bit. The first step was to include the library in the project and configure it.

private fun initKoin() {
    startKoin {
      androidContext([email protected])
      modules(koinModules)
    }
  }

Initially, the list of koinModules includes the instances of the most basic stateless common elements:

val koinModules = listOf(
  commonModule,
  networkModule,
  databaseModule
)

These modules include things like the ApiClient, the Room database, a label manager, or the analytics manager — things that any project feature might need to a greater or lesser extent.

The next step was to add the test to make sure the module definitions are correct. The main drawback of moving from Dagger to Koin is that Dagger warns on every build if we have done something wrong, on the other hand, Koin will fail only at runtime, so it is especially important to have a way to ensure the correctness of our modules. Luckily the way to test this in Koin is quite easy and we have a CI that runs all the tests before letting us release a version (either test or production).

class KoinModulesTest : KoinTest {

  @get:Rule
  @ExperimentalCoroutinesApi
  val coroutineRule = CoroutineMainDispatcherRule()

  @get:Rule
  val rule: TestRule = InstantTaskExecutorRule()

  @get:Rule
  val mockProvider = MockProviderRule.create { clazz ->
    mockkClass(clazz, relaxed = true)
  }


  @Test
  fun testKoinDependencies() {
    startKoin {
      androidContext(mockk(relaxed = true))
      modules(koinModules)
    }.checkModules {
      //Here we can define the parameters for the ViewModels
      create<FeatureViewModel> { parametersOf(mockk<Foo>(), mockk<Bar>()) }
      //We can also declare mocks
      declareMock<MyService>()
     }
    }
  }
}

With this single test, we can test the entire dependency tree. The only thing that requires some attention is the dependencies that require external parameters to the tree itself, such as a parameter that we pass from a fragment to its ViewModel. We may also need to declare other mocks, especially if there is a dependency that executes code at build time. For example:

val data = liveData {
    myService.getData(request)?.let { emit(it) }
  }

If we don’t mock the dependency, the test will end up having problems trying to call the real service.

The two rules that head the test class are to avoid problems with the coroutines, as in the previous example. If the getData method is suspendable, the test may end up failing even if the dependencies are well set up. The third rule is to define to Koin which mocking framework to use. In our case we use Mockk, but you could use mockito or any other framework you want.

Gradual Migration

We take advantage of new developments to use Koin for new features. It is easier to create the modules and dependencies in parallel. This can induce performance problems when we have, for example, the same ApiService instantiated twice. But except for the ViewModels, the rest of the classes are stateless, so it doesn’t affect the performance of the project. And as new features require new ViewModels, we don’t have the problem of having the same ViewModel injected in two different ways.

Each new feature will have a new module, and this is added to the list of modules defined at the beginning. For example, let’s imagine we have a new feature whose ViewModel receives a Foo and a Bar from the fragment and needs a FeatureService. The module would look like this:

val featureModule = module {
  single {
    FeatureService(get(), get())
  }
  //single<FeatureService>() if we can use reflection

  viewModel { (foo: Foo, bar: Bar) ->
    FeatureViewModule(foo, bar, get())
  }
}

By using an experimental Koin feature, we can save having to define the service parameters. This feature uses reflection so it may not be usable in all cases, but in our case, the impact on performance was not noticeable and we decided to keep it.

For existing features, the migration is similar. We define a Koin module equivalent to the one already defined in Dagger, add it to the module list, and change the fragment injection from:

 @Inject
  lateinit var viewModel: LegacyViewModel

to

  private val viewModel: LegacyViewModel by viewModel { parametersOf(Foo()) }

Another advantage that Koin offers is not having to declare the injected elements as lateinit var, making them clearer and safer. Using the by viewModel delegate, the ViewModel will be instantiated only in case it is needed.

Once a module is migrated, we can remove the @Inject constructor from the injected classes so that our production code doesn’t need to know anything about how parameters are passed. Only the Koin module definitions and our Android classes (Fragments, Activities) know anything about how dependencies are injected.

We can also stop inheriting from DaggerFragment and DaggerAppCompatActivity. Since Koin works by extensions, we don’t need to modify the parents of our classes.

Final Result

This migration took us some time. We started incrementally and we took advantage of a few features to finish the migration definitively. Once the migration was finished, we were left with a more idiomatic code and just as robust, as the CI ran the test and warned us of any errors.

The number of lines of code in the whole project decreased by about 3000 lines of code (about five percent of the total). With 3MB less code generated, now the only code generated is from Room, BuildConfig, and Navigation Component.

Most importantly, the build time was more than halved. We went from builds of more than 10 minutes to builds of less than five minutes.

We have no regrets at all about the effort involved in this migration. At the time, it was a significant time investment, but the time we have saved day by day has been more than worth it.



Source link

ShareSendTweet
Previous Post

Hisuian Voltorb Still Looks Like A Pokéball Somehow

Next Post

More details emerge on how Snap, Facebook, and others are skirting App Tracking Transparency

Related Posts

MongoDB vs DynamoDB Head-to-Head – DZone Database

July 1, 2022
0
0
MongoDB vs DynamoDB Head-to-Head – DZone Database
Software Development

Databases are a key architectural component of many applications and services. Traditionally, organizations have chosen relational databases like SQL Server,...

Read more

Understanding Kubernetes Resource Types – DZone Cloud

July 1, 2022
0
0
Understanding Kubernetes Resource Types – DZone Cloud
Software Development

Note: This is the first of a five-part series covering Kubernetes resource management and optimization. We start by describing Kubernetes...

Read more
Next Post

More details emerge on how Snap, Facebook, and others are skirting App Tracking Transparency

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

© 2021 GetUpdated – MW.

  • About
  • Advertise
  • Privacy & Policy
  • Terms & Conditions
  • Contact

No Result
View All Result
  • Home
  • Game Updates
    • Mobile Gaming
    • Playstation News
    • Xbox News
    • Switch News
    • MMORPG
    • Game News
    • IGN
    • Retro Gaming
  • Tech News
    • Apple Updates
    • Jailbreak News
    • Mobile News
  • Software Development
  • Photography
  • Contact
    • Advertise With Us
    • About

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms bellow to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In
Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?