Reputation: 3904
I want scope ViewModel and it's dependencies by Fragment not activity,
interface MarsRepository {
suspend fun getProperties(): List<Mars>
}
@Module
@InstallIn(FragmentComponent::class)
class MarsRepositoryModule {
@Provides
fun bindRemoteMarsDataSource(): MarsRepository{
return RemoteMarsStorageImp()
}
}
class MarsViewModel @ViewModelInject constructor(private val repository: MarsRepository): ViewModel()
@AndroidEntryPoint
class MarsFragment: Fragment() {
val viewModel by viewModels<MarsViewModel>()
}
But I got this
/app/build/generated/source/kapt/debug/com/Sample/bestcase/DevByteApplication_HiltComponents.java:111: error: [Dagger/MissingBinding] com.Sample.domain.repository.MarsRepository cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements DevByteApplication_GeneratedInjector,
^
javax.inject.Provider<com.Sample.domain.repository.MarsRepository> is injected at
com.Sample.bestcase.features.mars.MarsViewModel_AssistedFactory(repository)
com.Sample.bestcase.features.mars.MarsViewModel_AssistedFactory is injected at
com.Sample.bestcase.features.mars.MarsViewModel_HiltModule.bind(factory)
java.util.Map<java.lang.String,javax.inject.Provider<androidx.hilt.lifecycle.ViewModelAssistedFactory<? extends androidx.lifecycle.ViewModel>>> is injected at
androidx.hilt.lifecycle.ViewModelFactoryModules.ActivityModule.provideFactory(…, viewModelFactories)
@dagger.hilt.android.internal.lifecycle.DefaultActivityViewModelFactory java.util.Set<androidx.lifecycle.ViewModelProvider.Factory> is requested at
dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.ActivityEntryPoint.getActivityViewModelFactory() [com.Sample.bestcase.DevByteApplication_HiltComponents.SingletonC → com.Sample.bestcase.DevByteApplication_HiltComponents.ActivityRetainedC → com.Sample.bestcase.DevByteApplication_HiltComponents.ActivityC]
The following other entry points also depend on it:
dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.FragmentEntryPoint.getFragmentViewModelFactory() [com.Sample.bestcase.DevByteApplication_HiltComponents.SingletonC → com.Sample.bestcase.DevByteApplication_HiltComponents.ActivityRetainedC → com.Sample.bestcase.DevByteApplication_HiltComponents.ActivityC → com.Sample.bestcase.DevByteApplication_HiltComponents.FragmentC]
It just works if installed by ActivityComponent
@Module
@InstallIn(ActivityComponent::class)
class MarsRepositoryModule {
@Provides
fun bindRemoteMarsDataSource(): MarsRepository{
return RemoteMarsStorageImp()
}
}
As I track the code It uses DefaultActivityViewModelFactory instead of DefaultFragmentViewModelFactory
/**
* Hilt Modules for providing the activity level ViewModelFactory
*/
@Module
@InstallIn(ActivityComponent.class)
public abstract static class ActivityModule {
@NonNull
@Multibinds
abstract Map<String, ViewModelAssistedFactory<? extends ViewModel>> viewModelFactoriesMap();
@Provides
@IntoSet
@NonNull
@DefaultActivityViewModelFactory
static ViewModelProvider.Factory provideFactory(
@NonNull Activity activity,
@NonNull Application application,
@NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
viewModelFactories) {
// Hilt guarantees concrete activity is a subclass of ComponentActivity.
SavedStateRegistryOwner owner = (ComponentActivity) activity;
Bundle defaultArgs = activity.getIntent() != null
? activity.getIntent().getExtras() : null;
SavedStateViewModelFactory delegate =
new SavedStateViewModelFactory(application, owner, defaultArgs);
return new HiltViewModelFactory(owner, defaultArgs, delegate, viewModelFactories);
}
}
/**
* Hilt Modules for providing the fragment level ViewModelFactory
*/
@Module
@InstallIn(FragmentComponent.class)
public static final class FragmentModule {
@Provides
@IntoSet
@NonNull
@DefaultFragmentViewModelFactory
static ViewModelProvider.Factory provideFactory(
@NonNull Fragment fragment,
@NonNull Application application,
@NonNull Map<String, Provider<ViewModelAssistedFactory<? extends ViewModel>>>
viewModelFactories) {
Bundle defaultArgs = fragment.getArguments();
SavedStateViewModelFactory delegate =
new SavedStateViewModelFactory(application, fragment, defaultArgs);
return new HiltViewModelFactory(fragment, defaultArgs, delegate, viewModelFactories);
}
private FragmentModule() {
}
}
Upvotes: 0
Views: 2052
Reputation: 4712
The viewmodel is already scoped by its fragment. What the compiler tries to tell you is, that you are trying to inject your repository
inside a component, that outlives the fragment.
As it says in the component section here:
Each component is responsible for injecting a different type of Android class. This is shown in the table below:
In order to inject a dependency in a viewmodel, it has to be at least ActivityRetainedScoped
Change FragmentComponent::class
with ActivityRetainedComponent::class
or SingletonComponent::class
@Module
@InstallIn(SingletonComponent::class) // or at least ActivityRetainedComponent
class MarsRepositoryModule {
@Provides
fun bindRemoteMarsDataSource(): MarsRepository{
return RemoteMarsStorageImp()
}
}
Upvotes: 1