I have a viewmodel that looks like this:
class FilterByCategoryViewModel @ViewModelInject constructor(
private val dataManager: AppDataManager,
private val networkHelper: NetworkHelper,
private val category: String
) : ViewModel() { ...
}
I will get dataManager
and networkHelper
from the ApplicationModule
. But I need to pass category
as a runtime parameter. I tried the below approach but I got the error Could not resolve AssistedModule
.
https://stackoverflow.com/a/65375442/5742365
I have also tried creating a viewmodel factory as below:
val factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory() {
@NonNull
override fun <T : ViewModel?> create(@NonNull modelClass: Class<T>): T {
return FilterByCategoryViewModel(
category
) as T
}
}
filterByCategoryViewModel = ViewModelProvider(requireActivity(), factory).get(FilterByCategoryViewModel::class.java)
But then I had to edit the viewmodel to have the category
parameter in the constructor and the networkHelper
and dataManager
parameters injected using field injection (I'm not sure whether it works that way or not). But it still didn't work. The Viewmodel now looks as follows:
class FilterByCategoryViewModel @ViewModelInject constructor(
private val category: String
) : ViewModel() {
@Inject
lateinit var dataManager: AppDataManager
@Inject
lateinit var networkHelper: NetworkHelper
...
}
Now I get this build error when I run the app:
D:\Workspace\AndroidProjects\RecipeApp\app\build\tmp\kapt3\stubs\debug\com\neeraja\recipeapp\ui\viewmodel\FilterByCategoryViewModel.java:20: error: incompatible types: NonExistentClass cannot be converted to Annotation
@error.NonExistentClass()
^D:\Workspace\AndroidProjects\RecipeApp\app\build\tmp\kapt3\stubs\debug\com\neeraja\recipeapp\ui\viewmodel\FilterByCategoryViewModel.java:34: error: incompatible types: NonExistentClass cannot be converted to Annotation
@error.NonExistentClass()
^
FAILURE: Build completed with 2 failures.
1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:checkDebugDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class android.support.v4.app.INotificationSideChannel found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub$Proxy found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub$Proxy found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$1 found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyResultReceiver found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyRunnable found in modules core-1.3.2-runtime (androidx.core:core:1.3.2) and support-compat-26.1.0-runtime (com.android.support:support-compat:26.1.0)
Go to the documentation to learn how to <a href="d.android.com/r/tools/classpath-sync-errors">Fix dependency resolution errors</a>.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
==============================================================================
2: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)
I'm learning Hilt, MVVM, Kotlin by myself and feel so stuck with this. Any suggestions on achieving this?
CodePudding user response:
I am not sure about hilt, but I have done this same thing with Dagger2 using AssistedInject. Here is my implementation,
class MyViewModel @AssistedInject constructor(
@Assisted private val savedStateHandle: SavedStateHandle,
dataSource: RemoteDataSource
) : ViewModel() {
@AssistedInject.Factory
interface Factory : AssistedSavedStateViewModelFactory<MyViewModel> {
override fun create(savedStateHandle: SavedStateHandle): MyViewModel
}
val groupId = savedStateHandle.getLiveData("groupId", "")
val searchType = savedStateHandle.getLiveData("searchType", 1)
}
In fragment/activit
val defArgs = bundleOf("groupId" to groupId, "searchType" to searchType)
val factory = viewModelFactory.create(this, defArgs)
viewModel = ViewModelProvider(this, factory)[MyViewModel::class.java]
CodePudding user response:
I have finally used ViewModelProvider.Factory as follows:
class FilterByTypeViewModelFactory constructor(val dataManager: AppDataManager,
val networkHelper: NetworkHelper,
val category: String) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return FilterByCategoryViewModel(dataManager , networkHelper , category) as T
}
}
The ViewModel now looks as follows:
class FilterByCategoryViewModel @ViewModelInject constructor(
val dataManager: AppDataManager,
val networkHelper: NetworkHelper,
val category: String
) : ViewModel() {
...
}
And in the Fragment:
@AndroidEntryPoint
class FilterByTypeFragment : Fragment() {
@Inject lateinit var dataManager: AppDataManager
@Inject lateinit var networkHelper: NetworkHelper
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (arguments != null) {
category = arguments?.get("categoryId") as String
val factory = FilterByTypeViewModelFactory(dataManager, networkHelper, category)
filterByCategoryViewModel = ViewModelProvider(requireActivity(), factory).get(FilterByCategoryViewModel::class.java)
}
setupUI()
setupObserver()
}
...
}
This is working for me. I have just been learning hilt. I was confused with the options like using ViewModelProvider.Factory or AssistedInject. Although this solution worked for me, I would like to know more about AssistedInject and the situations where we need to choose one over the other.