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"
Related
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.
Cannot create an instance of class com.comp.app.winners.WinnersViewModel
Caused by: java.lang.InstantiationException: java.lang.Class<com.comp.app.winners.WinnersViewModel> has no zero argument constructor
Getting an error when trying to resolve a viewmodel on a fragment using hilt
// Proj
ext.hilt_version = '2.32-alpha'
ext.lifecycle_version = "2.2.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// App
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha03'
implementation "androidx.fragment:fragment-ktx:1.1.0"
#HiltAndroidApp
class MyApplication : Application()
#Module
#InstallIn(SingletonComponent::class)
class ApplicationModule {
#Provides
fun provideService(): MyService = MyServiceImpl()
}
#AndroidEntryPoint
class HomeActivity : AppCompatActivity() {
// Fragment is added here
private fun openFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().apply {
replace(R.id.container, fragment)
addToBackStack(null)
commit()
}
}
#AndroidEntryPoint
class WinnersFragment: Fragment() {
private val viewModel: WinnersViewModel by viewModels()
}
#HiltViewModel
class WinnersViewModel #Inject constructor(
private val service: MyService
) : ViewModel()
Is there something else to be done with the fragment?
Do i need to provide the viewModel somehow?
NOTE: This is a crash/runtime-error, not a compile error
My issue was nothing to do with my ViewModel, but to do with the fragment it was being applied to.
My ViewModel looks like
#HiltViewModel
class MyViewModel #Inject constructor(repository: MyRepository): ViewModel() {
This is correct, but I still got the exception Caused by: java.lang.InstantiationException: java.lang.Class<com.myapp.MyViewModel> has no zero argument constructor
However, I'd missed the AndroidEntryPoint annotation on the fragment. Adding this back in fixed the problem for me, i.e.
#AndroidEntryPoint
class MyFragment: Fragment() {
private val viewModel: MyViewModel by viewModels()
...
}
If getting this error while using compose, it might be because you are using a NavHost and NavHostController. According to official documentation:
If your #HiltViewModel annotated ViewModel is scoped to the navigation graph, use the hiltViewModel composable function that works with fragments or activities that are annotated with #AndroidEntryPoint.
hiltViewModel is part of a dedicated Hilt + Compose Navigation dependency:
dependencies {
// 1.0.0-beta01 at time of this writing.
// official docs linked above appear to auto update the version
implementation("androidx.hilt:hilt-navigation-compose:[version]")
}
Here is a contextualized example of the call to hiltViewModel provided in the same documentation:
// import androidx.hilt.navigation.compose.hiltViewModel
#Composable
fun MyApp() {
NavHost(navController, startDestination = startRoute) {
composable("example") { backStackEntry ->
// Creates a ViewModel from the current BackStackEntry
// Available in the androidx.hilt:hilt-navigation-compose artifact
val exampleViewModel = hiltViewModel<ExampleViewModel>()
ExampleScreen(exampleViewModel)
}
/* ... */
}
}
You need to upgrade to Fragment 1.2.0 or higher.
As per the Lifecycle 2.2.0 release notes, the new ViewModelProvider APIs that Hilt uses under the hood only apply when using Fragment 1.2.0 or higher. When using an older version of Fragments, those APIs are not connected to fragments and therefore your Hilt enabled ViewModel factory is not used when you use by viewModels().
You should upgrade to Fragment 1.2.5 (the last version of the Fragment 1.2.X set) or to Fragment 1.3.0, both of which contain the necessary API hooks to get Hilt working.
I try to create instance of ViewModel, but get an error.
java.lang.RuntimeException: Cannot create an instance of class com.example.event.ui.main.EventsViewModel
my code here to get instance:
viewModel = ViewModelProvider(this).get(EventsViewModel::class.java)
view model class
class EventsViewModel #Inject constructor(private val eventApi: EventApi): BaseViewModel() {
val getEventsData = MutableLiveData<Response<List<Event>>>()
fun getEventsData() {
uiScope.launch {
getEventsData.value = eventApi.getEvents()
}
}
}
Here, your ViewModelProvider is not aware of how to create EventsViewModel. If your ViewModel constructor was an empty constructor without any dependencies, it would work. However, if there is dependency or a parameter in your ViewModel constructor you need to provided a custom ViewModelProviderFactory.
If you want to check how/why custom view model factory is created you can check this codelab:
https://developer.android.com/codelabs/kotlin-android-training-view-model#7
If you want to employ dagger for injecting dependencies into your ViewModel, you can find an example in my sample repo:
https://github.com/hakanbagci/truemvvm
I want to initialize ViewModel in Activity using androidx library
I have tried what documentation says but it is not working. the ".of" is not resolved.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.example.myapplication.databinding.ActivityMainBinding`
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.setLifecycleOwner(this)
var model = ViewModelProvider.of(this).get(SheduleViewModel::class.java)
}
}
of is not resolved, maybe there are another way to do it in androidx
Updated answer:
Things changed a little bit, as the previously needed dependency - ViewModelProviders - got deprecated (see the old answer for details). You can now use the ViewModelProvider constructor directly.
So, in this case, the answer would be:
private val viewModel = ViewModelProvider(this).get(SheduleViewModel::class.java)
Note that, however, if you include the androidx.activity:activity-ktx:$Version dependency (a few of the commonly used AndroidX dependencies already include it for you), you can make use of property delegation:
private val viewModel: SheduleViewModel by viewModels()
Which internally will use ViewModelProvider and scope your ViewModel to your Activity. It's just a more concise way of writing the same thing. You can do the same for a Fragment by including the androidx.fragment:fragment-ktx:$Version dependency instead (again, commonly already included by other AndroidX dependencies).
Both the ViewModelProvider constructor and by viewModels() also accept a factory as a parameter (useful for injecting your ViewModel):
private val viewModel =
ViewModelProvider(this, viewModelFactory).get(SheduleViewModel::class.java)
and
private val viewModel: SheduleViewModel by viewModels { viewModelFactory }
Use the one that best suits you.
Old answer:
Add the androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionsVersion dependency in order to import ViewModelProviders.
Updating ViewModel to Lifecycle Version 2.2.0 and Above
The ViewModels (VMs) may theoretically be initialized as class level instance variables using the Kotlin extension library import androidx.fragment.app.viewModels method by viewmodels(). By initializing the VM as a class level instance var it can be accessed within the class.
Question: Is there a downside to initializing the VMs as class level instance variables instead of inside onCreate?
When creating the VMs with the extension function inside onCreate the VMs are only scoped within onCreate and extra code is required to reassign the class level instance variables.
See documentation
ViewModel Overview
Lifecycle
Initialize VM as Class Instance Val
class Fragment : Fragment() {
private val viewModel: SomeViewModel by viewModels()
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Initialize VM in onCreate and Reassign Class Instance Var
class Fragment : Fragment() {
private lateinit var viewModel: SomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: ContentViewModel by viewModels()
this.viewModel = viewModel
}
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Passing Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
Enabling SavedState with Arguments/Parameters
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
For me, the only thing that worked:
implementation 'androidx.fragment:fragment:1.2.4'
PS. This is for someone who is using java and got stuck for a while like I did and this SO answer comes up in google all the time.
Apparently, the API has change as of this date (6 May 2020), I had to do this to get it working.
// 1. Create a ViewModel Class Let's call it AppStateViewModel
// 2. Put below code Inside Activity onCreate like this:
ViewModelProvider.Factory factory = new ViewModelProvider.NewInstanceFactory();
appStateManager = new ViewModelProvider(this, factory).get(AppStateViewModel.class);
ViewModelProviders: This class is deprecated. Use the constructors for ViewModelProvider directly.
Examples in Kotlin
This is how you can use ViewModelProvider directly:
If your view-model is extending AndroidViewModel with just one argument, the app - then you can use the default AndroidViewModelFactory and you don't have to write a new Factory. Example:
// Activity / fragment class
private lateinit var viewModel: MyOwnAndroidViewModel
// onCreate
viewModel = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory(application)
).get(MyOwnAndroidViewModel::class.java)
If your view-model is only extending the ViewModel without extra arguments then use the NewInstanceFactory().
// Activity / fragment class
private lateinit var viewModel: MyOwnViewModel
// onCreate
viewModel = ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(MyOwnViewModel::class.java)
Adam's answer above covers other variations as well.
Disclaimer: Still learning basic Android development - if there's any problem with the code, let me know in comments.
(How to) Use ViewModel from Android Architecture Component :
Add the Google Maven repository (Optional, just verify that)
Android Studio projects aren't configured to access this repository by default.
To add it to your project, open the build.gradle file for your project (not the ones for your app or module) and add the google() repository as shown below:
allprojects {
repositories {
google()
jcenter()
}
}
Declaring dependencies
Open your app-level build.gradle file,
Go to dependencies{} block
Put implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" for AndroidX version, $lifecycle_version here is latest version defined.
For Pre-AndroidX use implementation "android.arch.lifecycle:viewmodel:1.1.1" (1.1.1 is the last version from this artifact i guess.)
In your activity, use like this syntax
Import this class :
import androidx.lifecycle.ViewModelProviders; for AndroidX
import android.arch.lifecycle.ViewModelProviders; when using Pre-AndroidX
And obtain your ViewModel like following
ViewModelProviders.of(this).get(ProfileObservableViewModel::class.java) // Kotlin syntax
---- or ----
ViewModelProviders.of(this).get(ProfileObservableViewModel.class); // Java syntax
In your app gradle file make sure you have added below dependencies:
For Activity use:
implementation "androidx.activity:activity-ktx:1.4.1"
For Fragment use:
implementation 'androidx.fragment:fragment:1.4.1'
Paste the code below in build.gradle(:app)
implementation 'androidx.fragment:fragment-ktx:1.4.1'
paste the following or similar(relevant to your settings) in app.gradle under dependencies
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
I add the last version of this dependency from
https://developer.android.com/kotlin/ktx/extensions-list
implementation "androidx.activity:activity-ktx:1.4.0"
Relating to the past question How to get a ViewModel?, and examples I've found, it does not solve my case. I don't have permission to comment there, so I have to ask again. I don't have the ViewModelProviders class which I would like to use the "of" method as in all the examples and documentation I've found, like
model = ViewModelProviders.of(this).get(Something.class);
In Android Studio, I do not find "android.arch.lifecycle:extensions" to add as a dependency. It says "nothing to show" when I search. I am at API 27.
So, trying to use the non-deprecated elements, I have this in the Fragment:
public class EventDetailsFragment extends Fragment
{
EventViewModel viewModel;
#Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication());
// HERE IS THE ISSUE: Seems not good practice to have to create a new ViewModelProvider every time this Fragment is created.
// Perhaps I should just create a singleton ViewModelProvider in the Activity or Application,
// so here could call getActivity().getViewModelProvider(this, factory).
ViewModelProvider viewModelProvider = new ViewModelProvider(this, factory);
viewModel = viewModelProvider.get(EventViewModel.class);
...
}
What should I do?
Open app/build.gradle, find the dependencies block and add the lifecycle dependency manually to the list:
dependencies {
implementation 'android.arch.lifecycle:extensions:1.1.1'
// ...
}
Sync the project and you should be able to get an instance of ViewModel afterwards.
Try to learn some MVVM architecture if u have better knowledge of Android and Java. Put this code into some baseFragment method and then make:
abstract class BaseFragment extends Fragment() {
//kotlin
fun <T : ViewModel?> getViewModel(activity: FragmentActivity, modelClass: Class<T>): T {
return ViewModelProviders.of(this, ViewModelFactory(activity)).get(modelClass)
}
//bytecode to java is this :/
public final ViewModel obtainViewModel(#NotNull FragmentActivity activity, #NotNull Class modelClass) {
return ViewModelProviders.of((Fragment)this, (Factory)(new
ViewModelFactory((Activity)activity))).get(modelClass);
}
}
also dont put this code into onViewCreated rather than to onActivityCreated
// kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = obtainViewModel(requireActivity(), KebabFragmentViewModel::class.java)
}
// java bytecode
public void onViewCreated(#NotNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.viewModel = (RollerFragmentViewModel)this.obtainViewModel(getFragmentActivity(), RollerFragmentViewModel.class);
}
and now u need observer for something from viewModel place in onViewCreated mby for some maybe network operation or something what can take longer period of time
// kotlin
viewModel.list.observe(this, Observer { list ->
list?.let {
viewModel.myKebabAdapter.updateList(list)
}
})
//java
this.viewModel.getErrorMessage().observe((LifecycleOwner)this, (Observer)(new Observer() {
...
public final void onChanged(#Nullable List list) {
this.viewModel.myKebabAdapter.updateKebabList(list)
}
}
and in viewModel u got our lovely variables for observing on activity/fragment
// kotlin
var list: MutableLiveData<MutableList<User>> = MutableLiveData()
// java variable and getter
#NotNull
private MutableLiveData userList;
#NotNull
public final MutableLiveData getUserList() {
return this.userList;
}
Now u need dependency app/build.gradle in dependencies{} block
//lifecycle
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
// RxJava
implementation "io.reactivex.rxjava2:rxkotlin:2.2.0"
implementation "io.reactivex.rxjava2:rxandroid:2.0.0"
implementation "io.reactivex.rxjava2:rxjava:2.1.10"
Find some blog tutorials how to properly do it. I personally dont like https://developer.android.com/ it used to be hard to follow. Try Medium.
GL. And Java code is mostly decompiled so hope it works.