Inject doesn't work with second constructor

Viewed 1333

for my current project I'm using Kotlin and Dagger 2. I want to inject dependencies in an secondary constructor, but the constructor never gets initialized.

class SelectionFragmentModel ():ViewModel(){
   lateinit var channelInfosRepository: ChannelInfosRepository
   @Inject constructor(channelInfosRepository: ChannelInfosRepository) : this(){
      this.channelInfosRepository = channelInfosRepository
   }
   ...
}

As a workaround I'm currently injecting in the primary constructor but this isn't optimal.

class SelectionFragmentModel @Inject constructor(private val channelInfosRepository: ChannelInfosRepository):ViewModel(){
   constructor() : this(ChannelInfosRepository())
   ...
}

Am I missing something?

1 Answers

Make sure your SelectionFragmentModel class has only one constuctor. It does not matter if the constructor is primary or secondary in terms of Kotlin language idioms. There will be only one constructor to use in SelectionFragmentModel.

The following code leaves no options for the initializer regarding which constructor to use as there is only one!

class SelectionFragmentModel: ViewModel {
    lateinit var channelInfosRepository: ChannelInfosRepository

    @Inject constructor(channelInfosRepository: ChannelInfosRepository) : super() {
        this.channelInfosRepository = channelInfosRepository
    }
}

Example (that works)

In this example we have default setup using dagger:

  1. @Module annotated class that provides us with ChannelInfosRepository;
  2. Modified SelectionFragmentModel (the code is located above the example);
  3. Interface annotated with @Component with a list of modules consisting of only one module;
  4. Everything else is just Android stuff.

Here is the module:

@Module
class AppModule {
    @Provides
    @Singleton
    fun providesChannelInfosRepository(): ChannelInfosRepository {
        return ChannelInfosRepository()
    }
}

Interface annotated with @Component:

@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
    fun inject(fragment: MainFragment)
}

Here is how the AppComponent is instantiated. MyApplication must be mentioned in AndroidManifest.xml.

class MyApplication : Application() {
    var appComponent: AppComponent? = null
        private set

    override fun onCreate() {
        super.onCreate()

        appComponent = DaggerAppComponent.builder()
                .appModule(AppModule())
                .build()
    }
}

Fragment that injects SelectionFragmentModel using AppComponent:

class MainFragment : Fragment() {

    @Inject
    lateinit var selectionFragmentModel: SelectionFragmentModel

    companion object {
        fun newInstance() = MainFragment()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Ugly but fine for a test
        (activity?.application as? MyApplication)?.appComponent?.inject(this)
    }

    // onCreateView and other stuff ...

    @SuppressLint("SetTextI18n")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        println("ViewModel address = ${selectionFragmentModel}")
        println("ChannelInfosRepository address = ${selectionFragmentModel.channelInfosRepository}")
    }
}

The result (instead of printing the result I displayed it in a TextView):

The result of the code in example

Related