android10 / Android-CleanArchitecture-Kotlin

This is a movies sample app in Kotlin, which is part of a serie of blog posts I have written about architecting android application using different approaches.
https://fernandocejas.com/2018/05/07/architecting-android-reloaded/
4.69k stars 935 forks source link

Replace Dagger 2 with Koin #39

Open stanbar opened 6 years ago

stanbar commented 6 years ago

Hello. Here is a fork where I did migration from Dagger2 to Koin. Take a look how much boilerplate code I was able to get rid of. Don't you think that it's much simpler, easier, faster and cleaner way ?

android10 commented 6 years ago

DI was out of scope of this, although that does not mean we can discuss about different approaches we can use. Also the fact we are using Dagger is an implementation details. :)

crjacinro commented 6 years ago

Hi @stasbar , Just curious on your experience in replacing Dagger2. Was it smooth ? or You did a lot of refactoring / changes when shifting the library used?

stanbar commented 6 years ago

@android10 You are right, it's implementation detail. But, I was curious if you chose Dagger2 purposely in this project, if it's the clean way of DI.

@crjacinro It was very smooth. I didn't add any new class, interfaces or anything like that. Here is great article about migration from Dagger to KOIN. Basically removed whole di package and created one file with modules, which is much simpler since you have everything in one place. (Main reason I don't like Dagger, is it classes complexity and high entry point). Additionally I removed all @Inject annotations, and changed way of injection from

@Inject lateinit var movieDetailsAnimator: MovieDetailsAnimator
private lateinit var movieDetailsViewModel: MovieDetailsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
    ...
    appComponent.inject(this)
    movieDetailsViewModel = viewModel(viewModelFactory)
    ...
}

to

private val movieDetailsAnimator: MovieDetailsAnimator by inject()
private val movieDetailsViewModel: MovieDetailsViewModel  by viewModel()

vals always on 👍

I'm quite experienced in KOIN and it's brother project Kodein, so it took me only about 20min.

Zhuinden commented 6 years ago

Okay, but by viewModel() creates the ViewModel with new instance factory, no? That's typically not what you want and want to use your own ViewModelProvider.Factory

stanbar commented 6 years ago

@Zhuinden from the documentation

The by viewModel() function lazily calls the ViewModel class instance and bind it to internal ViewModels factory.

And here is also implementation

fun <T : ViewModel> LifecycleOwner.getViewModelByClass(
    fromActivity: Boolean = false,
    clazz: KClass<T>,
    key: String? = null,
    name: String? = null,
    parameters: Parameters = emptyParameters()
): T {
    KoinFactory.apply {
        this.parameters = parameters
        this.name = name
    }
    val viewModelProvider = when {
        this is FragmentActivity -> {
            Koin.logger.log("[ViewModel] get for FragmentActivity @ $this")
            ViewModelProvider(ViewModelStores.of(this), KoinFactory)
        }
        this is Fragment -> {
            if (fromActivity) {
                Koin.logger.log("[ViewModel] get for FragmentActivity @ ${this.activity}")
                ViewModelProvider(ViewModelStores.of(this.activity), KoinFactory)
            } else {
                Koin.logger.log("[ViewModel] get for Fragment @ $this")
                ViewModelProvider(ViewModelStores.of(this), KoinFactory)
            }
        }
        else -> error("Can't get ViewModel on $this - Is not a FragmentActivity nor a Fragment")
    }
    return if (key != null) viewModelProvider.get(
        key,
        clazz.java
    ) else viewModelProvider.get(clazz.java)
}

where KoinFactory is singleton factory

object KoinFactory : ViewModelProvider.Factory