I have a class that relies on WeakReference<Fragment>.
class ExampleManager(reference: WeakReference<Fragment>)
How would I inject ExampleManager constructor?
val exampleModule = module {
factory { ExampleManager(get()) }
}
private val exmpManager: ExampleManager by inject()
At the end I receive error:
No definition found for class:'java.lang.ref.WeakReference'. Check your definitions!
How can I implement definition for WeakReference<Fragment> in my case?
In order to inject a WeakReference using Koin, you can create a factory function that creates the WeakReference and use it in your Koin module definition.
Here's an example:
// Factory function to create WeakReference<Fragment>
fun createWeakRef(fragment: Fragment) = WeakReference(fragment)
// Koin module definition
val exampleModule = module {
factory { (fragment: Fragment) -> ExampleManager(createWeakRef(fragment)) }
}
Then, you can use the by inject() delegate to inject the ExampleManager class in your activity or fragment:
class ExampleFragment : Fragment() {
private val exmpManager: ExampleManager by inject { parametersOf(this) }
// ...
}
Here, parametersOf(this) is used to pass the current fragment instance to the factory function.
It is important to mention that, WeakReference is a java class and you should import it using import java.lang.ref.WeakReference
Make sure you have included the created module in your Koin's startKoin function
i recommend to use'lazy' when you are injecting instances that are only used in certain conditions, this can help to avoid unnecessary instantiation and memory leaks.
private val exmpManager: ExampleManager by inject { parametersOf(this) }.lazy
This way, exmpManager will be instantiated only when it is accessed for the first time.
I have just learnt manual dependency injection, but I am trying out Hilt to handle these dependency injections.
I want to inject a ViewModel into a Fragment. The fragment is contained within an Activity. Right now, I have added the annotations to Application, Activity, and Fragment.
#HiltAndroidApp
class MovieCatalogueApplication : Application()
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
...
}
#AndroidEntryPoint
class HomeFragment : Fragment() {
private lateinit var binding: FragHomeBinding
private val viewmodel: HomeViewModel by viewModels()
...
As can be seen, my HomeFragment depends on HomeViewModel. I have added a ViewModel injection as described here like so.
class HomeViewModel #ViewModelInject constructor(
private val movieRepository: MovieRepository,
private val showRepository: ShowRepository,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
However, the ViewModel requires two repositories. Right now, my MovieRepository is like so.
class MovieRepository (private val movieApi: MovieService) {
...
}
In the above code, MovieService will be created by Retrofit using the Retrofit.create(class) method. The interface used to create MovieService is like so.
interface MovieService {
...
}
To get my Retrofit instance, I am using the following code.
object RetrofitService {
...
private var _retrofit: Retrofit? = null
val retrofit: Retrofit
get() {
return when (_retrofit) {
null -> {
_retrofit = Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
_retrofit!!
}
else -> _retrofit!!
}
}
}
I am not too sure how I can inject the Retrofit into the Repository to be used by my ViewModel later on. Could someone give me some pointers or step-by-step instructions on how to do this?
Apparently, it is not as hard as it seems.
You have to first define the binding information to Hilt. Binding information tells Hilt how to provide the instances of the dependency specified. Because MovieService is created using a Retrofit (which is a 3rd-party class not created by yourself) using the builder pattern, you can't use the constructor injection and you have to instead use Hilt modules and the annotation #Provides to tell Hilt about this binding information.
As described in the doc, the annotated function in the Hilt module you have created will supply the following information to Hilt so that Hilt can provide the instances of the dependency.
• The function return type tells Hilt what type the function provides instances of.
• The function parameters tell Hilt the dependencies of the corresponding type.
• The function body tells Hilt how to provide an instance of the corresponding type. Hilt executes the function body every time it needs to provide an instance of that type.
In the end, you only need to modify the MovieRepository class, add a module for each repository, and annotate the function that tells Hilt how to provide the service instance created with Retrofit with #Provides.
Code.
class MovieRepository #Inject constructor(
private val movieApi: MovieService
) {
...
}
interface MovieService {
...
}
#Module
#InstallIn(ActivityRetainedComponent::class)
object MovieModule {
#Provides
fun provideMovieService(): MovieService
= RetrofitService.retrofit.create(MovieService::class.java)
}
As you can see, the ActivityRetainedComponent is referred in the #InstallIn annotation because the Repository is to be injected to a ViewModel. Each Android component is associated to different Hilt components.
How do we inject ViewModel with dependency using Koin?
So For Example I have a ViewModel thats like this:
class SomeViewModel(val someDependency: SomeDependency, val anotherDependency: AnotherDependency): ViewModel()
Now the official docs here, states that to provide a ViewModel we could do something like:
val myModule : Module = applicationContext {
// ViewModel instance of MyViewModel
// get() will resolve Repository instance
viewModel { SomeViewModel(get(), get()) }
// Single instance of SomeDependency
single<SomeDependency> { SomeDependency() }
// Single instance of AnotherDependency
single<AnotherDependency> { AnotherDependency() }
}
Then to inject it, we can do something like:
class MyActivity : AppCompatActivity(){
// Lazy inject SomeViewModel
val model : SomeViewModel by viewModel()
override fun onCreate() {
super.onCreate()
// or also direct retrieve instance
val model : SomeViewModel= getViewModel()
}
}
The confusing part for me is that, normally you will need a ViewModelFactory to provide the ViewModel with Dependencies. Where is the ViewModelFactory here? is it no longer needed?
Hello viewmodel() it's a Domain Specific Language (DSL) keywords that help creating a ViewModel instance.
At this [link][1] of official documentation you can find more info
The viewModel keyword helps declaring a factory instance of ViewModel.
This instance will be handled by internal ViewModelFactory and
reattach ViewModel instance if needed.
this example of koin version 2.0 [1]: https://insert-koin.io/docs/2.0/documentation/koin-android/index.html#_viewmodel_dsl
// Given some classes
class Controller(val service : BusinessService)
class BusinessService()
// just declare it
val myModule = module {
single { Controller(get()) }
single { BusinessService() }
}
In my current project I use the next line:
mViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
For instance a ViewModel but in https://developer.android.com/reference/android/arch/lifecycle/ViewModelProviders.html#ViewModelProviders() recommend use ViewModelProvider.AndroidViewModelFactory because ViewModelProviders() was deprecated in API level 1.1.0.
any idea for this purpose?
If you had a simple ViewModel extending AndroidViewModel without any additional constructor parameters, its as follows
Extend AndroidViewModel without any additional constructor
parameters
class FooViewModel(application: Application) : AndroidViewModel(application) {}
Create View Model in Activity
val viewModel = ViewModelProvider(this).get(FooViewModel::class.java)
But if you had a ViewModel extending AndroidViewModel with any additional constructor parameters, its as follows
Extend AndroidViewModel with any additional constructor
parameters
class FooViewModel(application: Application, foo: Foo) : AndroidViewModel(application) {}
Create a new view model factory extending ViewModelProvider.AndroidViewModelFactory
class FooViewModelFactory(val application: Application, val foo: Foo): ViewModelProvider.AndroidViewModelFactory(application) {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return FooViewModel(
application, foo
) as T
}
}
Create View Model in Activity
val viewModel = ViewModelProvider(this, FooViewModelFactory(application, foo)).get(FooViewModel::class.java)
EDIT: The original question is now irrelevant, as you should no longer use the ViewModelProviders utility class. Instead, you should create a ViewModelProvider instance like so:
val viewModel = ViewModelProvider(thisFragment).get(MyViewModel::class.java)
Original answer below.
ViewModelProviders is just a utility class with static methods, there's no need to instantiate it (there are no instance methods in it anyway), so the constructor being deprecated shouldn't be a concern.
The way you use it is by calling its appropriate of method for your use case, passing in a Fragment or Activity, and then calling get on the ViewModelProvider it returns:
val viewModel = ViewModelProviders.of(thisFragment).get(MyViewModel::class.java)
If you don't provide your own factory in the second parameter of the of method, AndroidViewModelFactory will be used by default. This implementation can either create ViewModel subclasses that have no constructor parameters, or ones that extend AndroidViewModel, like such:
class MyViewModel(application: Application) : AndroidViewModel(application) {
// use application
}
you can try this code
ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(UserViewModel::class.java)
You can use AndroidViewModelFactory like this:
mViewModel = ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory(application))
.get(MainViewModel::class.java)
So, it is quite simple:
// This is the deprecated way to create a viewModel.
viewModel = ViewModelProviders.of(owner).get(MyViewModel::class.java)
//This is the deprecated way to create a viewModel with a factory.
viewModel = ViewModelProviders.of(owner, mViewModelFactory).get(MyViewModel::class.java)
// This is the new way to create a viewModel.
viewModel = ViewModelProvider(owner).get(MyViewModel::class.java)
//This is the new way to create a viewModel with a factory.
viewModel = ViewModelProvider(owner, mViewModelFactory).get(MyViewModel::class.java)
Open build.gradle(Module:~.app)
Edit appcompat version to 1.3.0-alpha02
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
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"