Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HILT : lateinit property repository has not been initialized in ViewModel

I am facing this issue in multi module android project with HILT.

 kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized in MyViewModel

My modules are

  1. App Module
  2. Viewmodel module
  3. UseCase Module
  4. DataSource Module

'App Module'

@AndroidEntryPoint
class MainFragment : Fragment() {
private lateinit var viewModel: MainViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View {
    return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    viewModel.test()
}}

'ViewModel Module'

class MainViewModel @ViewModelInject constructor(private val repository: MyUsecase): ViewModel() {
fun test(){
    repository.test()
}}

'UseCase Module'

class MyUsecase @Inject constructor() {

@Inject
lateinit var feature: Feature

fun doThing() {
    feature.doThing()
}

@Module
@InstallIn(ApplicationComponent::class)
object FeatureModule {
    @Provides
    fun feature(realFeature: RealFeature): Feature = realFeature
}
}

'DataSource Module'

interface Feature {
fun doThing()
}

class RealFeature : Feature {
override fun doThing() {
    Log.v("Feature", "Doing the thing!")
}
}

Dependencies are

MyFragment ---> MyViewModel ---> MyUseCase ---> DataSource

what i did wrong with this code pls correct it.

like image 525
Arun Avatar asked Jul 10 '20 02:07

Arun


3 Answers

above your activity class you must add annotation @AndroidEntryPoint as below:

@AndroidEntryPoint class MainActivity : AppCompatActivity() {

like image 113
Mosayeb Masoumi Avatar answered Nov 14 '22 02:11

Mosayeb Masoumi


In addition to moving all your stuff to constructor injection, your RealFeature isn't being injected, because you instantiate it manually rather than letting Dagger construct it for you. Note how your FeatureModule directly calls the constructor for RealFeature and returns it for the @Provides method. Dagger will use this object as is, since it thinks you've done all the setup for it. Field injection only works if you let Dagger construct it.

Your FeatureModule should look like this:

@Module
@InstallIn(ApplicationComponent::class)
object FeatureModule {
    @Provides
    fun feature(realFeature: RealFeature): Feature = realFeature
}

Or with the @Binds annotation:

@Module
@InstallIn(ApplicationComponent::class)
interface FeatureModule {
    @Binds
    fun feature(realFeature: RealFeature): Feature
}

This also highlights why you should move to constructor injection; with constructor injection, this mistake wouldn't have been possible.

like image 45
David Liu Avatar answered Nov 14 '22 02:11

David Liu


The problem in the code is that @ViewModelInject doesn't work as @Inject in other classes. You cannot perform field injection in a ViewModel.

You should do:

class MainViewModel @ViewModelInject constructor(
  private val myUseCase: MyUsecase
): ViewModel() {

  fun test(){
    myUseCase.test()
  }
}

Consider following the same pattern for the MyUsecase class. Dependencies should be passed in in the constructor instead of being @Injected in the class body. This kind of defeats the purpose of dependency injection.

like image 1
manuelvicnt Avatar answered Nov 14 '22 03:11

manuelvicnt