Using ViewModel default constructor Versus via ViewModelProvider class? - android

I have an Android ViewModel
class MyVM: ViewModel()
In my activity, I can create an instance of MyVM using either:
val viewModel = MyVM()
or using:
val viewModel = ViewModelProvider(this).get(MyVM::class.java)
They both work as expected. My question is what are the main differences between the two

Related

How to inject dependency in composable using dagger hilt

Maybe I'm blind but I can't find anything about injecting a dependency that needs parameters in side a composable using dagger hilt.
Lets say my ViewModel looks something like this:
class MyViewModel #AssistedInject constructor(#Assisted myValue: Int) : ViewModel() {
...
}
and I've got a factory interface like this:
#AssistedFactory
interface MyViewModelAssistedFactory {
fun create(myValue: Int): MyViewModel
}
how can I inject that dependency with a certain value as parameter?
All answers I found where like:
#Inject
var myViewModelFactory: MyViewModelAssistedFactory;
and
val initValue = 4
fun onCreate(){
val viewModel = myViewModelFactory.create(initValue)
}
but that doesn't work inside a composable fun.
Not sure if still relevant but if you use the navigation component you can just call hiltViewModel() in the navgraph builder.
Example:
https://github.com/pablichjenkov/ComposeStudy/blob/04298ca8393d3eea0f5b7883fb223161ef79a962/app/src/main/java/com/pablichj/study/compose/home/HomeNavigation.kt#L24
If not using Jetpack Navigation then the solution is a bit more complex. You will need to create a State tree in your App where they implement LifecycleOwner and ViewModelStoreOwner, in order to be able to install the ViewModel appropriately. The good news is that there is work out there already doing so, check this:
https://github.com/Syer10/voyager

I'm trying to implement MVVM Architecture, but this error is consuming me... (ViewModelProvider)

just it's me trying to implement MVVM architecture with Kotlin. I need to use ViewModelProvider to init my instance and I'm getting this error...
Any advice?
You have a no of options to to instantiate viewmodels.
Now You can now use the ViewModelProvider constructor directly.
private val assignmentViewModel= ViewModelProvider(this).get(AssignmentViewModel::class.java)
If you use androidx.activity:activity-ktx:$Version in the library you can do this directly
private val assignmentViewModel: AssignmentViewModel by viewModels()
If you're using a viewModelFactory
private val assignmentViewModel= ViewModelProvider(this, viewModelFactory).get(AssignmentViewModel ::class.java)
and private val assignmentViewModel: AssignmentViewModel by viewModels { viewModelFactory } respectively
If you're using Kotlin the easiest way to incorporate a ViewModel in your code is via a property delegate:
Activity/Fragment:
val assignmentViewModel: AssignmentViewModel by viewModels()
Fragment:
val assignmentViewModel: AssignmentViewModel by activityViewModels()
The latter allows you to share data between fragments, since activityViewModels scopes it to the Activity.
See ViewModel Overview for more information.

Hilt - what is the Java equivalent to kotlin's "by viewmodels()" to inject viewmodel into activity?

I'm following Kotlin tutorial on learning Dagger Hilt for dependency injection. The tutorial uses
class MainActivity: AppCompatActivity() {
private val viewModel: TestViewModel by viewModels()
}
in the MainActivity to inject the viewmodel.
It requires a dependency: implementation "androidx.activity.activity-ktx:1.1.0" to do so.
I'm trying to learn hilt in Java so I'm unsure what the Java equivalent of injecting the viewmodel into my activity is.
This is incorrect and doesn't work
#Inject
private TestViewModel testViewModel;
and using
testViewModel = new ViewModelProvider(this).get(TestViewModel.class);
doesn't seem like dependency injection.
What is the equivalent of by viewModels() in Java?
testViewModel = new ViewModelProvider(this).get(TestViewModel.class) is indeed exactly what by viewModels() does for you.
You'll note that both by viewModels() and new ViewModelProvder(this) don't pass in a custom ViewModelProvider.Factory instance - that's because they use the default Factory - the one setup by Hilt to correctly create your ViewModel via DI.

AndroidViewModel with parameters. Why is this working and could it cause future issues

The reason why I am asking is because I've read that using a Factory is how you're supposed to pass arguments into a ViewModel, yet this implementation seems to be working fine.
My ViewModel
class MyListViewModel(application : Application , currentItemName : String) : AndroidViewModel(application) {
val dataSource = Database.getInstance(application).DatabaseDao
val itemList = dataSource.getItems(currentItemName)
internal val _singleItem = MutableLiveData<Item>()
val singleItem : LiveData<Item>
get() = _singleItem
}
And here is how I initiate it in my Fragment
val currentItemName = "some item name"
val binding = FragmentItemsListBinding.inflate(inflater)
val application = requireNotNull(this.activity).application
val viewModel = MyListViewModel(application, currentItemName)
binding.lifecycleOwner = this
binding.viewModel = viewModel
I'm happy if there are no downsides to this, but am a little concerned about creating other problems.
yet this implementation seems to be working fine
Perhaps you did not test a configuration change, such as:
Rotating the device between portrait and landscape
Toggling on or off dark mode
Switching the locale in Settings
And so on
The point behind using the Jetpack ViewModel system is to deal with configuration changes. And, for that, you need to use ViewModelProvider and let it manage creation of the ViewModel, whether using a built-in Factory, your custom one, or one provided by a dependency inversion framework (Dagger/Hilt, Koin, etc.).

Koin sharedViewModel with SavedStateHandle

I have single activity application and number of fragments. Some of these fragments are using my viewmodel, typically like this:
private val myViewModel: MyViewModel by sharedViewModel()
What if I want to have the model both shared and keep its state with SavedStateHandle? I cannot figure out if this is supported and if so, how it needs to be used (declaring viewmodel as stateViewModel in hosting activity is not working).
Update: as koin 2.1.6 is around, they introduced org.koin.androidx.viewmodel.ext.android.stateSharedViewModel that you can use in your fragments.
Ok after an hour of digging Koin samples and figuring out a few gotchas:
Assuming your view model is something similar to this:
class SavedStateViewModel(val handle: SavedStateHandle, val service: SimpleService)
...and your DI looks like this:
viewModel { (handle: SavedStateHandle) -> SavedStateViewModel(handle, get()) }
Your shared state view model can be consumed in your fragments like this:
val sharedSaved: SavedStateViewModel by sharedViewModel()
(important!) You need this declaration in your activity:
lateinit var savedVm: SavedStateViewModel
(important) You need to call this right after super.onCreate(savedInstanceState) in your activity:
savedVm = getStateViewModel()
It is important not to use lazy version for the above (stateViewModel).

Categories

Resources