architecture-samples icon indicating copy to clipboard operation
architecture-samples copied to clipboard

Using Hilt2.48.1 version, it is not compatible with BaseViewModel

Open cl-6666 opened this issue 2 years ago • 1 comments

The BaseActivity I use encapsulates BaseViewModel, and an error is reported during compilation. The code and error message are as follows error message: public abstract static class SingletonC implements AppApplication_GeneratedInjector, ^ Injection of an @HiltViewModel class is prohibited since it does not create a ViewModel instance correctly. Access the ViewModel via the Android APIs (e.g. ViewModelProvider) instead. Injected ViewModel: com.maxvision.myapplication.YourViewModel

  com.maxvision.myapplication.YourViewModel is injected at
      com.maxvision.myapplication.base.BaseActivity.viewModel
  com.maxvision.myapplication.MainActivity is injected at
      com.maxvision.myapplication.MainActivity_GeneratedInjector.injectMainActivity(com.maxvision.myapplication.MainActivity) [com.maxvision.myapplication.AppApplication_HiltComponents.SingletonC → com.maxvision.myapplication.AppApplication_HiltComponents.ActivityRetainedC → com.maxvision.myapplication.AppApplication_HiltComponents.ActivityC]

code show as below: `abstract class BaseActivity <VM : BaseViewModel, DB : ViewDataBinding> : AppCompatActivity() {

@Inject
lateinit var viewModel: VM
lateinit var binding: DB

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, getLayoutResId())
    binding.lifecycleOwner = this
}

//获取布局资源id
abstract fun getLayoutResId(): Int

}`

` @HiltViewModel class YourViewModel @Inject constructor( private val apiService: ApiService ) : BaseViewModel() {

}

`

` @AndroidEntryPoint class MainActivity : BaseActivity<YourViewModel, ActivityMainBinding>(){

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}

override fun getLayoutResId(): Int = R.layout.activity_main

} @HiltAndroidApp class AppApplication : Application() {

override fun onCreate() {
    super.onCreate()

}

companion object {
    private val sInstance: Application? = null
}

} `

cl-6666 avatar Nov 27 '23 02:11 cl-6666

The issue you're facing stems from how Hilt handles @HiltViewModel-annotated classes and how you're trying to inject your ViewModel directly into BaseActivity. This is not allowed in Hilt 2.48.1 (or any recent Hilt version), because:

You cannot inject a @HiltViewModel ViewModel using @Inject lateinit var viewModel: VM directly.

Instead, Hilt requires you to use the ViewModelProvider APIs to create and retrieve @HiltViewModel classes properly, so it can attach the correct lifecycle scope.

Solution You need to remove field injection for your ViewModel in BaseActivity and use ViewModelProvider to get the ViewModel like this:

Step-by-Step Fix

  1. Remove @Inject from the viewModel field

abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : AppCompatActivity() {

protected lateinit var viewModel: VM
protected lateinit var binding: DB

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, getLayoutResId())
    binding.lifecycleOwner = this
}

abstract fun getLayoutResId(): Int
abstract fun createViewModel(): VM

} 2. Initialize ViewModel in your subclass using ViewModelProvider

@AndroidEntryPoint class MainActivity : BaseActivity<YourViewModel, ActivityMainBinding>() {

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

    viewModel = createViewModel()
    // Now you can use viewModel here
}

override fun getLayoutResId(): Int = R.layout.activity_main

override fun createViewModel(): YourViewModel {
    return ViewModelProvider(this)[YourViewModel::class.java]
}

} Alternatively, you can use Kotlin property delegate:

private val viewModel: YourViewModel by viewModels() But this won’t work generically in BaseActivity, so keep it in subclasses.

Why It Fails with @Inject Hilt uses a special mechanism (@HiltViewModel) that internally uses SavedStateHandle and lifecycle-aware scoping. Trying to inject the ViewModel directly bypasses this mechanism, and causes:

Injection of an @HiltViewModel class is prohibited...

VaradGupta23 avatar Jul 15 '25 13:07 VaradGupta23