Cannot create an instance of class ViewModel with constructor - android

I'm trying to use the viewmodel in my activity but my app crashes the error "Cannot create an instance of class" from the viewmodel. The ViewModel is like this:
class MyViewModel#Inject constructor(val application: Application) : ViewModel() {
//...
}
In my activity, I have this:
class Activity: BaseActivity(){
val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
If I delete the constructor, my app works but I need to get packageName, so I need context or application.
Why I'm getting this error? Any idea?

You can use AndroidViewModel
class MyViewModel#Inject constructor(val application: Application) : AndroidViewModel(application)

If you using Dagger-hilt may b you can't add #HiltViewModel. also check in your activity whether this tag (#AndroidEntryPoint) is added or not.

Related

Why it says "Cannot create an instance of class com.app.myapp.viewModel" in android jetpack compose?

I am new in adroid , so I have a simple project, I want to create simple register project, so I have viewmodel in my project and I amusing Hilt library also in there, and when I build project it is throw an error for
myViewModel = ViewModelProvider(this)[MyViewModel::class.java]
as a "Cannot create an instance of class com.app.myapp.viewModel", I do not know what I missed?
class Register : ComponentActivity() {
private lateinit var myViewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myViewModel = [ViewModelProvider(this)::class.java]
setContent {
RegisterScreen(myViewModel)
}
}
}
#Composable
fun RegisterScreen(
myViewModel: MyViewModel
) {
}
Reasons may cause system can not create viewModel:
Your viewModel class is not Public
Your package name which contains viewModel contains special keywords (such a "package.new.feature")
If you are using dagger hilt you should putt annotation #HiltViewModel above the class declaration and create constructor like
#HiltViewModel
class viewModel #Inject constructor() : ViewModel()
With the dagger hilt You should use hiltViewModel() function to create instance for compose instead of viewModel()
dependency: androidx.hilt:hilt-navigation-compose
#Composable
fun MyExample (viewModel: MyViewModel = hiltViewModel())
Your ViewModel class does not extend from androidx.lifecycle.ViewModel
You should create your ViewModel class extending from the ViewModel, something like RegisterViewModel.
Take a look at the documentation for more info:
https://developer.android.com/topic/libraries/architecture/viewmodel
You are trying to create a view model from the base class ViewModel. it doesn't work like this
You need to create your own viewmodel class and extend it from the base class ViewModel like this
class MyViewModel : ViewModel() {
}
So your code will be like
class MyViewModel : ViewModel() {
// your implementation
}
class Register : ComponentActivity() {
private lateinit var viewModel: MyViewModel // changes to this line
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this)[MyViewModel::class.java] // changes to this line
setContent {
RegisterScreen(viewModel)
}
}
}
BUT if you are using compose you should look at the integration between viewmodel and compose
to make your composable use the viewModel without you creating it then passing it to the composable
#Composable
fun MyExample(
viewModel: MyViewModel = viewModel()
) {
// use viewModel here
}

Hilt - EntryPoint in Fragment

I am using Hilt for DI and I have this class.
class ChatCore #Inject constructor()
This class needs to be injected in fragment , without marking the fragment as #AdroidEntryPoint as this fragment can be attached to activity which isn't marked as #AndroidEntryPoint
How can i achieve this. I tried using EntryPoint but i end up with error.
class MyFragment : Fragment() {
lateinit var chatCore: ChatCore
#EntryPoint
#InstallIn(FragmentComponent::class)
interface ChatCoreProviderEntryPoint{
fun chatCore():ChatCore
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val hiltEntryPoint = EntryPointAccessors.fromFragment(this, ChatCoreProviderEntryPoint::class.java)
chatCore = hiltEntryPoint.chatCore()
}
Solved it by adding it into the application container.
#EntryPoint
#InstallIn(ApplicationComponent::class)
interface ChatCoreProviderEntryPoint{
fun chatCore():ChatCore
}
val hiltEntryPoint = EntryPointAccessors.fromApplication(applicationContext,
ChatCoreProviderEntryPoint::class.java)
If you don't want to use AndroidEntryPoint for your Fragment you need to #Install your module (containing your dependency) within a different Component.
E.g. within the ApplicationComponent not the FragmentComponent.
Then you will also need to use the corresponding EntryPointAccessors.fromXyz(...) method. E.g. for a module installed in the ApplicationComponent you should be using EntryPointAccessors.fromApplication(...).

Android Kotlin - inherit a VIewModel from another ViewModel

I've created a structure in app with BaseActivity and BaseViewModel. All other activities/viewModels must be extend with this base classes. I made that cause i need to call some methods in any activity (like showInfo() method).
When i update LiveData in BaseViewModel and observe it in BaseActivity all works well. But when i update that LiveData in child ViewModel (e.g. UsersViewModel) only with BaseActivity observing its won't work.
What should i do when i want to call some base method in any activity through ViewModel?
open class BaseActivity : AppCompatActivity() {
//inject viewModel with Koin
private val baseViewModel: BaseViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
baseViewModel.actionShowInfo.observe(this, Observer {
showInfo(it)
}
}
protected fun showInfo(message: String) {
AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton(R.string.ok, null)
.show()
}
}
open class BaseViewModel : ViewModel() {
private val actionShowInfo = MutableLiveData<String>()
init {
actionShowInfo.postValue("some base info") //showInfo() in BaseActivity will be called
}
}
class UsersActivity : BaseActivity() {
private val usersViewModel: UsersViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(
}
}
class UsersViewModel: BaseViewModel {
init {
//showInfo() in BaseActivity will not be called
actionShowInfo.postValue("some info")
}
}
Just by extend the UserViewModel your BaseViewModel, doesn't mean it's sharing the same instance. Based on your requirement, I think you need a ViewModel that can share it's instance to several activity, so that when you update the ViewModel on Activity A, you can observe the change on Activiy B, and so on.
This is where SharedViewModel come to rescue. You need to implement a sharedViewModel to all your activity.
private val baseViewModel: BaseViewModel by sharedViewModel()
Reference: https://doc.insert-koin.io/#/koin-android/viewmodel?id=shared-viewmodel

how to inject dependency outside activity or fragment in Kodein or Koin?

want to initialize interface in a non activity or fragment class with Kodein DI Android
sample shows only hot to use Kodein inside activity, but not on the other parts
class MainViewModel() : KodeinAware{
override val kodein by closestKodein()
val repository : Repository by instance()
}
in activity it works, but in other classes it shows error.
I want to initialize interface inside another class
closestKodein only works in Android Context aware classes (such as fragments & activities).
To use it outside of these classes, you need an Android context.
The android documentation clearly states:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
[...]
If the ViewModel needs the Application context, for example to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.
Therefore, to access Kodein from a ViewModel:
class MainViewModel(app: Application) : ApplicationViewModel(app), KodeinAware {
override val kodein = app.kodein
val repository : Repository by instance()
}
Simpy pass a context or activity as param
override val kodein by closestKodein(context)
More info https://kodein.org/Kodein-DI/?5.0/android#_getting_a_kodein_object
Use it in any place. appKodein is global function.
val dataLayer: DataLayer = appKodein().instance()
override val kodein by kodein(activity!!)
class ReportViewModel(context: Context):ViewModel() ,KodeinAware
{
override val kodein by kodein(context)
val reportRepository:ReportRepository by instance()
}
My answer

Koin how to inject outside of Android activity / appcompatactivity

Koin is a new, lightweight library for DI and can be used in Android as well as in standalone kotlin apps.
Usually you inject dependencies like this:
class SplashScreenActivity : Activity() {
val sampleClass : SampleClass by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
with the inject() method.
But what about injecting stuff in places where Activity context is not available i.e. outside of an Activity?
There is the KoinComponent which comes to the rescue. In any class you can simply:
class SampleClass : KoinComponent {
val a : A? by inject()
val b : B? by inject()
}
Extending KoinComponent gives you access to inject() method.
Remember that usually it's enough to inject stuff the usual way:
class SampleClass(val a : A?, val b: B?)
Koin provides a solution for this using the KoinComponent interface. For example, if you need to get some dependencies in your repository then you can simply implement the KoinComponent interface. It gives you access to various Koin features such as get() and inject(). Use KoinComponent only when you can't rewrite the constructor to accept dependencies as constructor parameters.
class MyRepository: Repository(), KoinComponent {
private val myService by inject<MyService>()
}
Constructor injection is better than this approach.
For example, the same thing can be achieved by:
class MyRepository(private val service: MyService): Repository() {
...
}
And you can add the definition for instantiating this class in a koin module:
val serviceModule = module {
...
factory { MyService() }
}
val repositoryModule = module {
...
factory { MyRepository(get<MyService>()) }
}
If you don't want to implement any interfaces then just take a look at how KoinComponent.inject() is implemented and do something similar yourself:
val foo by lazy { KoinPlatformTools.defaultContext().get().get<FooClass>() }

Categories

Resources