I'm trying to inject some dependency to both activity and fragment using Koin and I expect it to live as long as activity lives, but it turned out a headache for me.
I managed to create a module that resolves MainRouter, inject it into an activity, but it doesn't work for a fragment.
val appModule = module {
scope<MainActivity> {
scoped { MainRouter() }
}
}
MainActivity extends ScopeActivity, MyFragment extends ScopeFragment.
in MainActivity private val router : MainRouter by inject() works fine, but in MyFragment it throws org.koin.core.error.NoBeanDefFoundException: No definition found for class:'com.example.app.MainRouter'. Check your definitions!
Finally I managed to inject, but it doesn't look pretty
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val scopeId = scopeActivity!!.getScopeId()
scope.linkTo(getKoin().getScope(scopeId))
mainRouter = get()
...
I also don't like that scopeActivity can't be accessed in the init method. Does this mean that activity scoped dependencies cannot be resolved in fragment using by inject()?
As I can see in your code, you have to declare a Fragment instance, just declare it as a fragment in your Koin module and use constructor injection. Like below:
val appModule = module {
single { MyService() }
fragment { MyFragment(get()) }
}
Please refer link for more details.
I wondered if it's possible to pass a String data which has declared in Activity class and pass the String data to ViewModel class then pass the data to Fragment class.
ViewModel Class
class TimeTableViewModel extends ViewModel {
private MutableLiveData<String> start_time_str = new MutableLiveData<>();
void send_StartTime(String start_Time){
start_time_str.setValue(start_Time);
}
LiveData<String> get_StartTime(){
return start_time_str;
}}
In ViewModel Class, I have MutableLiveData<String> start_time_str and it has been initialized as new MutableLiveData<>();
I would like to use void send_StartTime(String start_Time) function in Activity class to set value of argument String start_Time and call the start_time_str in Fragment class.
Activity Class
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case android.R.id.home:
finish();
break;
case R.id.add_schedule_save:
String start_time_str = startTime.getText().toString();
Intent intent_restart0 = new Intent(TimeTable_Add_New_Schedule.this, MainActivity.class);
startActivity(intent_restart0);
TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
timeTableViewModel.send_StartTime(start_time_str);
Toast.makeText(this,""+start_time_str,Toast.LENGTH_LONG).show();
break;
}
return super.onOptionsItemSelected(item);
}
Fragment Class
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
timeTableViewModel.get_StartTime().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(String s) {
mon_textView_11.setText(s);
}
});
}
In the Fragment class I call get_StartTime() function to get start_time_str and set the String value to my TextView. I think the start_time_str has been successfully set by function of timeTableViewModel.send_StartTime(start_time_str); in the Activity Class because of Toast.maketext is worked like a charm. However the TextView is not shown anything. I have tested Text Color is not white so that if the string value is correctly called, it should be appear on screen. If you have any suggestions, I would love to hear your advice.
Thank you very much.
It really depends on how do you create your ViewModel instance. Now you are creating ViewModel by its constructor, but that is not a proper way. You should use ViewModelProvider or extension methods that were created by Google team.
If you go with ViewModelProvider you should do it like this:
TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);
It is important to pass the correct context to ViewModelProvider constructor call. If you are in fragment and you will just use getContext() instead of getActivity(), you will not get the same instance as it was created in Activity. You will create a new instance of ViewModel, that will be scoped only inside of fragment lifecycle. So it is important to use in both parts activity context to get the same instance.
Activity part:
TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);
Fragment part:
TimeTableViewModel viewModel = new ViewModelProvider(getActivity()).get(TimeTableViewModel.class);
Is important that your fragment is located inside the same activity that is using this ViewModel.
But guys at Google has make it easier for us with some extension methods. But as far as I know, they are working only in Kotlin classes. So if you have Kotlin code, you can declare your ViewModel simply like this:
private val quizViewModel: TimeTableViewModel by activityViewModels()
For Fragment scoped ViewModel you need to write something like this:
private val quizViewModel: TimeTableViewModel by viewModels()
But you have to add Kotlin ktx dependency to your project build.gradle file. For example like this:
implementation 'androidx.fragment:fragment-ktx:1.1.0'
If you are using Android Architecture and want to share activityViewModel in your fragments.
To get viewModels in fragment use below code:
private val fragmentViewModel: Fragment1ViewModel by viewModels()
private val activityViewModel: MainActivityViewModel by activityViewModels()
and in MainActivity use below code:
private val activityViewModel: MainActivityViewModel by viewModels()
I'm using kotlin language
I'm used same as tutorial but not working
Here's example codes how to declare my ViewModel
add dependencies in build.gradle:
implementation 'androidx.fragment:fragment-ktx:1.4.1'
example of Activity class
class HomepageActivity : AppCompatActivity() {
private lateinit var sharedViewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomepageBinding.inflate(layoutInflater)
setContentView(binding.root)
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
sharedViewModel.isMenuOpen.observe(this, {
onMenuOpen(it)
})
}
example in Fragment class
class HomeFragment : Fragment() {
private lateinit var sharedViewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel = activity!!.run{
ViewModelProvider(this).get(SharedViewModel::class.java)
}
}
I found the simplest fix to this problem, instead of giving the owner as "this" change it to getActivity().
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mvm == null) {
mvm = new ViewModelProvider(getActivity()).get(CounterFgViewModel.class);
}
}
I have been spending a lot of time trying to figure out why in the code below (towards the end), I get an error on ViewModelProvider(this). I also tried getActivity() instead of 'this', same issue. The error I get is "Cannot resolve constructor ..."
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
public class ItemSetupFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_setup, container, false);
return view;
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ItemSetupFragmentModel model = new ViewModelProvider(this).get(ItemSetupFragmentModel.class);
model.getKids().observe(this, users -> {
// update UI
});
}
}
Firstly you need to use the latest version of lifecycle extension. It should be:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
or any updated version.
Then you should use requireActivity() instead of getActivity(). This way you will ensure that the activity is attached an not getting a NullPointerException.
ItemSetupFragmentModel model = new ViewModelProvider(requireActivity()).get(ItemSetupFragmentModel.class);
Note: ViewModel Overview and Declaring Dependencies
I had to restart cache after adding the library to the Gradle file.
There is no need to use requireActivity(), this is enough.
You aren't using the latest library release in which the ViewModelProvider(#NonNull ViewModelStoreOwner owner) constructor was included. You are seeing the latest docs but not using the latest library version of ViewModel.
You need to use
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0' // For Java
or
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' // For kotlin extension
The cleanest implementation must use Kotlin for its advanced functions. You can either create this kotlin code in a separate Kclass or leave this answer for future users that want to know how to do this in Kotlin. Basically we are initialising the ViewModel by lazy like this:
Make sure you have this dependency:
implementation "androidx.fragment:fragment-ktx:1.2.0"
Create this helper function that accesses an internal fragment-ktx method that allows yo to create a ViewModel instance by lazy:
#MainThread
inline fun <reified VM : ViewModel> Fragment.fragmentViewModel() =
createViewModelLazy(
VM::class,
{ this.viewModelStore },
{ ViewModelFactory(Database.getDatabase(requireContext().applicationContext)) }
)
Now create a ViewModelFactory using this official java example:
https://github.com/android/architecture-components-samples/blob/master/BasicRxJavaSample/app/src/main/java/com/example/android/observability/ui/ViewModelFactory.java
Or, here is the Kotlin variant:
class ViewModelFactory(private val database: Database?) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
requireNotNull(database) { "Database must not be null" }
return when {
modelClass.isAssignableFrom(ItemSetupFragmentModel::class.java) -> {
ItemSetupFragmentModel() as T
}
else -> {
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}
}
And now go inside your fragment and simply initialise your ViewModel like this
class ItemSetupFragment : Fragment() {
private val model by viewModel<ItemSetupFragmentModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.getKids().observe(this, users -> {
// update UI
});
}
}
Hope this helps!
You should instantiate your viewModel by :
ItemSetupFragmentModel model = ViewModelProviders.of(this).get(ItemSetupFragmentModel.class);
I'm using lifecycle version 2.2.0-rc03 and the official docs and articles found don't even list the correct class name or constructor arguments. I think I have to get the ViewModel instance through something like this
viewModel = ViewModelProvider(this, SavedStateViewModelFactory(requireActivity().application, savedStateRegistryOwner))
.get(SelectedTracksViewModel::class.java)
but I can't figure out the SavedStateRegistryOwner.
Can someone give a simple example of how to create the saved state ViewModel instance and the correct way to save and restore a value in the ViewModel?
For using Saved State module for View Model you have to add the androidx.lifecycle:lifecycle-viewmodel-savedstate dependency to your project. This example has been written based on version 1.0.0-rc03.
Please add the following line to your project Gradle file:
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-rc03'
ViewModel implementation:
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {
val liveData = state.getLiveData("liveData", Random.nextInt().toString())
fun saveState() {
state.set("liveData", liveData.value)
}
}
Activity implementation:
class SavedStateActivity : AppCompatActivity() {
lateinit var viewModel: SavedStateViewModel;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityStateBinding = DataBindingUtil.setContentView(this, R.layout.activity_state)
viewModel = ViewModelProvider(this, SavedStateViewModelFactory(this.application, this)).get(SavedStateViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = this
}
override fun onSaveInstanceState(outState: Bundle) {
if(::viewModel.isInitialized)
viewModel.saveState()
super.onSaveInstanceState(outState)
}
}
I have tested this code and it works fine.
I am adding an answer to this old post just in case someone might find it useful.
I managed to do it as follows:
Add the following dependency to your "build.gradle (Module: app)" file
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
Add savedState: SavedStateHandle property to the constructor of the ViewModel
class SelectedTracksViewModel(private val savedState: SavedStateHandle) : ViewModel() {
companion object {
private const val SAVED_TRACK_INDEX = "savedTrackIndex"
}
private var trackIndex: Int
set(value) {
field = value
// Simply update the savedState every time your saved property changes
savedState.set(SAVED_TRACK_INDEX, value)
}
init {
trackIndex = savedState.get<Int>(SAVED_TRACK_INDEX) ?: 0
}
fun moveToNextTrack() {
trackIndex++
// Initially I was updating savedState here - now moved to setter
// Some more code here
}
}
Finally in the activity/fragment
private val selectedTracksViewModel: SelectedTracksViewModel by lazy {
ViewModelProvider(this).get(SelectedTracksViewModel::class.java)
}
And that's it. No need for SavedStateViewModelFactory, simply add the savedState property to your ViewModel constructor and update it when tracked properties change. Everything else works as if you're not using savedState: SavedStateHandle and this way is very similar to the traditional onSaveInstanceState(Bundle) in activities/fragments.
Update: Initially I was updating savedState after changing trackIndex. This means one has to update savedState every time saved properties are changed. This is a huge potential future bug if one forgets to add that line. A better and more robust pattern is to update the savedState in the setter of the property.
As far as I understand you want to create View model with spec constructor.
You can use ViewModelProvider.Factory.
viewModel = ViewModelProvider(this, SavedStateViewModelFactory.create(state)
.get(SelectedTracksViewModel::class.java)
example of ViewModelFactory
public class SavedStateViewModelFactory {
public static <E> ViewModelProvider.Factory create(State state){
return new ViewModelProvider.Factory() {
#NonNull
#Override
#SuppressWarnings("unchecked")
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(SelectedTracksViewModel.class)) {
return (T) new SelectedTracksViewModel<>(state);
} else {
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
};
}
}
I am trying to write a sample app using Android architecture components and but even after trying for days I could not get it to work. It gives me the above exception.
Lifecycle owner:-
public class MainActivity extends LifecycleActivity {
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.tv_user);
PostViewModel viewModel = ViewModelProviders.of(this).get(PostViewModel.class);
viewModel.loadPosts();
viewModel.getPost().observe(this, new Observer<Post>() {
#Override
public void onChanged(#Nullable Post post) {
if(post != null) {
textView.setText(post.toString());
}
}
});
}
}
ViewModel:-
public class PostViewModel extends ViewModel {
private MediatorLiveData<Post> post;
private PostRepository postRepo;
PostViewModel() {
post = new MediatorLiveData<>();
postRepo = new PostRepository();
}
public LiveData<Post> loadPosts() {
post.addSource(postRepo.getPost(),
post -> this.post.setValue(post)
);
return post;
}
#NonNull
public LiveData<Post> getPost() {
return post;
}
}
if you are using Hilt, ensure your activity/fragment is having #AndroidEntryPoint annotation
Make your constructor public.
If you are using Kotlin make sure to replace any annotationProcessor in build.gradle with kapt.
Like:
annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
Will become
kapt "android.arch.persistence.room:compiler:$rootProject.roomVersion"
and add
apply plugin: 'kotlin-kapt' on top of the buidl.gradle file.
Annotation Processing with Kotlin
Make sure your ViewModel has constructor with only one parameter i.e. Application.
example:
public YourViewModel(Application application) {
super(application);
...
DaggerHilt can also be the reason, If you are using it make sure your activity/fragment is having #AndroidEntryPoint annotation on it.
There are few reason to throw the exception . I have mention some of them..
Make sure your view Model class is public
Make sure your view model class constructor is public
Make sure you have added the dependency in your gradle file for
lifecycle also if you use room and other libraries you have added ..
if you create object any other dependent class in your view model
class constructor . Other class can throw error to create the
instance of viewModel
I had this problem following google's roomdb CodeLab. Solution was changing the following.
Edited
Add the following Build dependencies to Gradle file (as of 2/22/2020)
implementation 'androidx.fragment:fragment:1.2.2'
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
implementation 'androidx.lifecycle:lifecycle-service:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0'
Imports within the fragment
import androidx.lifecycle.ViewModelProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
Creating the viewModel. Add one of the following methods.
Note: I'v seen this done many ways. I believe the correct way is using getDefaultViewModelProviderFactory(). But I have been using requireActivity().
new ViewModelProvider(requireActivity(),getDefaultViewModelProviderFactory()).get(YourViewModel.class);
|
new ViewModelProvider(requireActivity()).get(YourViewModel.class);
ViewModelProvider Docs
Deprecated
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc01'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0-rc01'
Add #HiltViewModel on top of your viewModel .
For people using Jetpack Compose, Navigation and Hilt
Make sure to use the hiltNavGraphViewModel instead of viewModel.
This is provided by androidx.hilt:hilt-navigation-compose dependency.
More details in the docs.
It was not completely obvious to me, but when getting this error I resolved it by creating a public constructor. My constructor was derived from the Android Developer examples and contained the Repository as a parameter. Creating an additional constructor that was empty with no params and having it public solved the issue.
i.e., in your case
public PostViewModel() {}
Make the class and constructor public it solved my problem .
Extend AndroidViewModel from your ViewModel class.
public class YourViewModel extends AndroidViewModel {
public YourViewModel(Application application) {
super(application);
//Todo: ...
}
}
If you used viewmodel inside your activity check that your activity extends "DaggerAppCompatActivity" or not
For instance
public class UserComments extends AppCompatActivity
change this to
public class UserComments extends DaggerAppCompatActivity
Mostly, Solution is making Class and Constructor Public as the other answers
It may also be a runtime error, check the Logcat Error Logs if there are multiple causes listed.
If you are using Hilt then don't forget to add these four dependencies.
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha01"
Note:- If any of these dependencies are missing you will get Cannot create an instance of class ViewModel error
In my case, the reason was that I was trying to get a shared instance of the ViewModel in my fragment too soon - before the activity was created. What happens when the application is restoring its state after being killed.
Preconditions:
My ViewModel has a public constructor.
My ViewModel has multiple arguments. But this is absolutely fine as I use ViewModelFactory to construct the ViewModel.
My Fragment and Activity shares the same instance of the ViewModel. In other words: Activity creates the ViewModel and the fragment receives the same instance later.
Code in activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//factory is constructed using Dagger
val factory = App.get().components().appComponent.getMapViewModelFactory()
//activity creates the instance of MapViewModel
viewModel = ViewModelProviders.of(this, factory)[MapViewModel::class.java]
}
Code in fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//fragment receives the instance of MapViewModel
viewModel = ViewModelProviders.of(activity!!)[MapViewModel::class.java]
...
}
When I open the app for the first time, everything works fine: activity creates an instance of ViewModel; I open Fragment, which gets the instance of ViewModel. But when the application is trying to restore its state after being killed, first it calls the body of onCreate of the Fragment and then the body of onCreate of the Activity. At that point, the fragment can't get the ViewModel as Activity had not created it yet.
Solution 1: Move the code when the fragment gets the ViewModel from onCreate to onViewCreated. This is fine as I observe all live data in onViewCreated as well.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = activity?.run { ViewModelProviders.of(this)[MapViewModel::class.java] } ?: throw Exception("Invalid Activity")
viewModel.getSurveyDateLiveData().observe(viewLifecycleOwner, Observer<String> { dateTextView.text = it })
...
}
Solution 2:
Create the instance of ViewModel in Activity.onCreate before super.onCreate is called. In this case, you can get the ViewModel in your fragment's onCreate.
override fun onCreate(savedInstanceState: Bundle?) {
val factory = App.get().components().appComponent.getMapViewModelFactory()
viewModel = ViewModelProviders.of(this, factory)[MapViewModel::class.java]
super.onCreate(savedInstanceState)
Timber.d("cc: onCreate: $this ")
}
Solution 3:
If you are injecting repository instance in your ViewModel, Check that you are not using #Inject constructor(...): ViewModel() to inject your repository, but rather **#ViewModelInject constructor(...): ViewModel()**
I fixed the same problem by doing this.
Note:- I am using Dagger hilt, Room database, MVVM, Data binding
Added the annotation.
class AlertViewModel
#Inject
constructor(private val userRepository: AlertRepository) : ViewModel(){
val getList:LiveData<List<Alert>> get() =
userRepository.getList.flowOn(Dispatchers.Main)
.asLiveData(context = viewModelScope.coroutineContext)
fun insert(user:Alert){
viewModelScope.launch {
userRepository.insert(user)
}
}
}
To
#HiltViewModel // Added this annotation
class AlertViewModel
#Inject
constructor(private val userRepository: AlertRepository) : ViewModel(){
val getList:LiveData<List<Alert>> get() =
userRepository.getList.flowOn(Dispatchers.Main)
.asLiveData(context = viewModelScope.coroutineContext)
fun insert(user:Alert){
viewModelScope.launch {
userRepository.insert(user)
}
}
}
I got this after migrating to AndroidX.
There's a bug in androidx.lifecycle:lifecycle-viewmodel:2.0.0-beta01 where Proguard removes the constructor.
https://issuetracker.google.com/issues/112230489
Fix by upgrading to 2.0.0, and remember to update your proguard rules if needed.
My error message looked like this:
java.lang.RuntimeException: Cannot create an instance of class my.custom.viewmodel.CustomViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:202)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:135)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:103)
......
Caused by: java.lang.NoSuchMethodException: <init> [class android.app.Application]
at java.lang.Class.getConstructor0(Class.java:2204)
at java.lang.Class.getConstructor(Class.java:1683)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:200)
... 34 more
androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 539, 1167 and precision: 16, 16' on view 'with id: my.test:id/button_return_to_main_menu'.
at androidx.test.espresso.PerformException$Builder.build(PerformException.java:82)
at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:79)
.....
Caused by: java.lang.RuntimeException: Unable to start activity ComponentInfo{my.custom.domain.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class my.custom.viewmodel.CustomViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
if your PostViewModel class is an inner class, make sure its public and static
Please add below code. It worked for me
val binding = FragmentLayoutBinding.inflate(inflater, container, false)
val viewModel = ViewModelProvider(
requireActivity(),
defaultViewModelProviderFactory
).get(MainViewModel::class.java)
If you face this issue in Kotlin Dagger Hilt even after #HiltViewModel and using #Inject, make sure you have updated all hilt dependencies.
In my case I needed to use a ListItemViewModelFactory to pass in a parameter to my view model.
In my case, it was gradle a dependencies problem.
If you are using Livedata,,
build.gradle(Module.app)
not
implementation 'android.arch.lifecycle:extensions:1.1.1'
kapt 'android.arch.lifecycle:common-java8:1.1.1'
use these
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
kapt 'androidx.lifecycle:lifecycle-common-java8:2.2.0'
If you're using Hilt Dependency Injection, You probably have missed #ViewModelInject. Because, Hilt provide its own injection for viewmodel.
In my case, I used and #Inject due to this caught into the error.
I had a different scenario when creating a view model instance:
I was requesting for the instance in a fragment.
My ViewModel required a parameter to be passed on the constructor.
I was not using Dependency Injection.
Solution
In a scenario where your viewmodel requires a parameter to be passed you have to create a ViewModelFactory to define your instances
Solution In Practice
- ViewModel Sample
class SharedViewModel(private val repository: UserRepository) : ViewModel() {
init {
viewModelScope.launch {
repository.refreshDataInDb()
}
}
}
- Creating ViewModel Factory
class ViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.NewInstanceFactory(){
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return SharedViewModel( repository as UserRepository) as T
}
}
- Creating ViewModel Instannce in a Fragment
private lateinit var factory: ViewModelFactory
private lateinit var searchViewModel: SharedViewModel
private lateinit var repository: UserRepository
repository = UserRepository()
factory = ViewModelFactory(repository)
searchViewModel = ViewModelProvider(requireActivity(), factory)[SharedViewModel::class.java]
My problem was that the IDE had added a "abstract" modifier to my ViewModel class.
Make ViewModal class and constructure public
If constructor of your viewmodel is public and accepts only application then make sure you can create your own model without ViewModelProvider. Error message might be much more clear:
val model = YouViewModel(app)
I'm a proficient Android developer and I have used ViewModel 100s of times with no issue. Today I came across this issue. Spent hours and scrolled through various SO posts. Didn't get solved.
Then I saw that the package name in which I have the ViewModel contains new. Like this:
com.myapp.myfeature.new.feature
I changed new to neww for testing like this:
com.myapp.myfeature.neww.feature
and it worked! I hope someone find it useful.
If you are using version 2.33-beta and upper remove these dependencies;
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0-beta01"
Keep only these two dependency
implementation "com.google.dagger:hilt-android:2.33-beta"
kapt "com.google.dagger:hilt-android-compiler:2.33-beta"