Reputation: 3850
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;
}
}
Upvotes: 168
Views: 189493
Reputation: 21
I got this exception only for one out of several similar view models in my kotlin project. The reason is that the (super)package was named new
, i.e .app.new.viewmodel
. This makes Hilt crash with a very obscure exception. Since new
is valid name, I got no errors neither from the IDE nor from the compiler.
Upvotes: 1
Reputation: 1802
Add @HiltViewModel
on top of your viewModel .
Add @AndroidEntryPoint
to top of your Activity/Fragment .
Upvotes: 31
Reputation: 19844
In my case I was consuming a library which contained the Hilt VM. My own app was using a slightly different version of hilt and the hilt gradle plugin (2.51 where the SDK was on 2.49).
This version difference seems to mean that the app can't "see" generated hilt files, so ensure those hilt versions match up if you are consuming a library.
Upvotes: 1
Reputation: 51
Make sure you have used @HiltViewModel annotation for your view model class.
@HiltViewModel
class CategoryViewModel : ViewModel() {
}
if you are using the below libraries implementation("androidx.navigation:navigation-compose:2.7.7") implementation("androidx.navigation:navigation-compose:2.7.7") then you should create an instance like this
val categoryViewModel : CategoryViewModel = hiltViewModel()
not like this
val categoryViewModel : CategoryViewModel = viewModel()
Upvotes: 1
Reputation: 1143
none of the above was my problem. I did added @HiltViewModel
and @AndroidEntryPoint
and @HiltAndroidApplication
like i had before and my code worked. the problem was that my project was multi-module and when i added the annotations it actually imported theme from other submodule but because the module didn't have those dependency in its own gradle.build it didn't compile, so i added these as same as other modules and it worked:
// Hilt
implementation("com.google.dagger:hilt-android:2.47")
kapt("com.google.dagger:hilt-compiler:2.44.2")
Upvotes: 1
Reputation: 3349
DaggerHilt can also be the reason, If you are using it make sure your activity/fragment
is having @AndroidEntryPoint
annotation on it.
Upvotes: 32
Reputation: 1067
In case you are using Jepack Compose with a ViewModel
in your component.
The @Preview
annotation may cause this error. That was the problem in my case.
Upvotes: 1
Reputation: 2167
Not for OP's case, but adding it here for newcomers.
If you inherit from AndroidViewModel and there is some heavy work in the main Thread (like accessing the database) it'll wrongly throw this error.
After I switched to inherit from ViewModel, it showed the correct error and I could move the heavy work to Dispatchers.IO
. After moving UI blocking stuff to Dispatchers.IO
, I re-tested using AndroidViewModel and everything works fine again.
Conclusion: "UI blocking work on the main thread when inheriting from AndroidViewModel can throw this error".
Upvotes: 1
Reputation: 55
Well this will fix the issue for sure
First of all make sure you have this dependency
//Room DB
implementation "androidx.room:room-runtime:2.2.5"
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
Then remove viewmodel constructor, then create init function as your constructor
public void init(Application application){
instance = new FirebaseDatabaseInstance();
databaseRepository = new DatabaseRepository(application);
allChatMembers = databaseRepository.getAllChatMembers();
}
Then this will solve...
Upvotes: 1
Reputation: 121
I had a different scenario when creating a view model instance:
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]
Upvotes: 1
Reputation: 11477
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)
}
}
}
Upvotes: 3
Reputation: 2007
If you face this issue in Kotlin Dagger Hilt even after @HiltViewModel and using @Inject, make sure you have updated all hilt dependencies.
Upvotes: 3
Reputation: 14004
Make sure to use the hiltNavGraphViewModel
instead of viewModel
.
This is provided by androidx.hilt:hilt-navigation-compose
dependency.
More details in the docs.
Upvotes: 25
Reputation: 1684
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"
Upvotes: 0
Reputation: 346
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.
Upvotes: 1
Reputation: 11989
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
Upvotes: 15
Reputation: 2896
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:
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()**
Upvotes: 4
Reputation: 680
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.
Upvotes: 0
Reputation: 2192
Please add below code. It worked for me
val binding = FragmentLayoutBinding.inflate(inflater, container, false)
val viewModel = ViewModelProvider(
requireActivity(),
defaultViewModelProviderFactory
).get(MainViewModel::class.java)
Upvotes: 2
Reputation: 9231
if you are using Hilt
, ensure your activity/fragment is having @AndroidEntryPoint
annotation
Upvotes: 290
Reputation: 3642
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
Upvotes: 3
Reputation: 596
I had this problem following google's roomdb CodeLab. Solution was changing the following.
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);
Deprecated
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc01'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0-rc01'
Upvotes: 13
Reputation: 6855
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)
Upvotes: 0
Reputation: 715
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'
Upvotes: 1
Reputation: 3661
My problem was that the IDE had added a "abstract" modifier to my ViewModel class.
Upvotes: 0
Reputation: 902
In my case I needed to use a ListItemViewModelFactory to pass in a parameter to my view model.
Upvotes: 1
Reputation: 880
if your PostViewModel class is an inner class, make sure its public and static
Upvotes: 2
Reputation: 115
I'm using this example android-arcuitecture-component BasicSample to make a new project, facing a similar error log, found I did'n change de applicatio name
AndroidManifest.xml
and that was my error, to fix put the aplicacion name to de BasicApp, this class is implement in the example.
...
<application
android:name=".BasicApp"
android:allowBackup="false"
Upvotes: -1
Reputation: 813
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
Upvotes: 5