rockstar4095
rockstar4095

Reputation: 23

Dagger with Kotlin in Android: lateinit property viewModelProviderFactory has not been initialized

My code:

  1. App Component:
@Component(
    modules = [
        AndroidSupportInjectionModule::class,
        ActivityBuildersModule::class,
        ViewModelFactoryModule::class
    ]
)
interface AppComponent : AndroidInjector<BaseApplication> {

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }
}
  1. ActivityBuildersModule:
@Module
abstract class ActivityBuildersModule {

    @ContributesAndroidInjector(
        modules = [MainActivityViewModelModule::class]
    )
    abstract fun contributeMainActivity(): MainActivity
}
  1. MainActivityViewModelModule:
@Module
abstract class MainActivityViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityViewModel::class)
    public abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityViewModel): ViewModel
}
  1. MainActivityViewModel:
class MainActivityViewModel @Inject constructor(application: Application) : AndroidViewModel(application) {
   // empty for now
}
  1. ViewModelKey (in Java):
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {

    Class<? extends ViewModel> value();
}
  1. MainActivity:
class MainActivity : AppCompatActivity() { <-- I made mistake here. I must Extend DaggerAppCompatActivity instead of AppCompatActivity.

//  doesn't work for now
    @Inject
    lateinit var viewModelProviderFactory: ViewModelProviderFactory

    lateinit var mainActivityViewModel: MainActivityViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        mainActivityViewModel = ViewModelProvider(this, viewModelProviderFactory).get(MainActivityViewModel::class.java)

        setMainFragment()
    }

    private fun setMainFragment() {
        // my code
    }
}

It gives me an error: lateinit property viewModelProviderFactory has not been initialized.

The same code works fine in another project in Java.

Upvotes: 1

Views: 249

Answers (2)

Henrique Vasconcellos
Henrique Vasconcellos

Reputation: 1183

I can be wrong here, but I think you need a ViewModelFactory in your viewModelModule, like this:

@Module
abstract class MainActivityViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityViewModel::class)
    public abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityViewModel): ViewModel


    @Binds
    abstract fun provideViewModelFactory(
        factory: ViewModelFactory
    ): ViewModelProvider.Factory

}
class ViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }

    }
}

Upvotes: 0

Karan Jhinga
Karan Jhinga

Reputation: 64

Extend DaggerAppCompatActivity instead of AppCompatActivity

Upvotes: 1

Related Questions