ViewModel in architect component - android - android

I have a lot of fragment base architect component.
Is this true that I create ViewModel for per fragment? or I should create one ViewModel for all fragment?

Saw many project that uses ViewModel for each Activity, and they pass them to their fragments if needed.
Same goes for me, but figure out what functions that Activity will do, and then build a ViewModel based on that functions:
Activity/Fragments that create an object.
Activity/Fragment that fetch a list.
Activity/Fragment that deal with Objects, like delete, update.
You can pass ViewModel to fragments. Also you can use inheritance with your ViewModels.

Related

How do I store a data from a fragment so it can be reuse in a MVVM architecture?

I have know that an Activity/Fragment has to create a ViewModel, and the ViewModel can be created from a ViewModelFactory. And the ViewModel itself are using a data repository which handles the data from either database or network. And the ViewModel is not a singleton.
For example, I have an activity which has two fragment, fragment A and fragment B, and I'm only able to access them one by one. In the fragment A, I load some data from a repository which came from a network or database. When I navigate to fragment B, the data in a fragment A is lost, so I have to load it back from a network or database which takes time. Because of that I would like to store my data somewhere in the runtime.
My question, what is the best approach to solve this problem? Is it okay to create a singleton in the repository?
you need to use a shared viewModel, a ViewModel that is shared between your fragments and survives navigating between certain fragments.
Implementation depends on what you use in your project. you can create a ViewModel in the activity and access them from your fragments and put the shared data in this ViewModel. or if you are using navigation component you can have a shared ViewModel per nav graph. and with dagger and koin you can define a costume scope for your ViewModel to survive. see these links:
Share data between fragments
Share data between fragments with shared viewModel

How to use a shared ViewModel, and avoid reusing the same instance of it every time with Navigation Component

My app consists of one single Activity with multiple Fragments, following the "single Activity app model", so that I can implement properly navigation using the Navigation Component in Android jetpack.
Most of my screens (Fragments) are standalone and don't depend on each other, hence they use their own ViewModel
Some features require navigation involving more than one Fragment. As those features share data between them that are passed back and forth through the Fragments, I use a shared ViewModel (as recommended by Google).
I need to use the same instance of the shared ViewModel in all the associated Fragments, as I need the Fragments share the state of the shared ViewModel.
To use the same instance of the ViewModel in these associated Fragments, I need to create the ViewModel using the parent Activity (not the Fragment) when getting the ViewModel from the ViewModelProviders:
val viewModel = ViewModelProviders.of(
parentActivity, factory.create()
).get(SharedViewModel::class.java)
This works, however, It produces one problem:
when navigating a consecutive times to the first Fragment that requires the shared ViewModel, ViewModelProviders.of() will return the same instance of the ViewModel as before: The ViewModel is being shared between the Fragments, but also between different navigations to the feature implemented like this.
I understand why this is happening (Android is storing the ViewModel in a map, which is being used when requesting the ViewModel with ViewModelProviders.of()), but I don't know how I am expected to implement the "shared ViewModel pattern" properly.
The only workarounds I see are:
Create a different Activity for the feature that uses the Fragment with a shared ViewModel
Use nested Fragments, and use common parent Fragment for the feature that uses the Fragment with a shared ViewModel
With these two options, I would be able to create a ViewModel that will be shared between the Fragments intervening in the feature, and will be different each time I navigate to the feature.
The problem I see here is that this seems to be that against the fundamentals of the Navigation Component and the single Activity app.
Each feature implemented this way will need to have a different navigation graph, as they will use a different navigation host. This would prevent me from using some of the nice features of Navigation Component.
What is the proper way to implement what I want?
Am I missing anything, or is it the way it is?
Before Navigation Component I would use different Activities and Fragments and use Dagger scopes associated with the Activity/Fragment to achieve this. But I'm not sure what's the best way of implementing this with just one Activity`
I have discovered this can be done starting with 2.1.0-alpha02
From:
https://developer.android.com/jetpack/androidx/releases/navigation#2.1.0-alpha02
You can now create ViewModels that are scoped at a navigation graph
level via the by navGraphViewModels() property delegate for Kotlin
users or by using the getViewModelStore() API added to NavController.
b/111614463
Basically:
in the nav graph editor, create a nested graph, assigning one id to it
when providing the ViewModel, do not do it from the Activity. Instead, use
the navGraphViewModels extension function of Fragment.
Example:
Nested graph in the nav graph
<navigation
android:id="#+id/feature_nested_graph"
android:label="Feature"
app:startDestination="#id/firstFragment">
<argument
android:name="item_id"
app:argType="integer" />
<fragment
android:id="#+id/firstFragment"
[....]
</fragment>
[....]
</navigation>
For getting the ViewModel scoped to feature_nested_graph nested nav gaph:
val viewModel: SharedViewModel
by fragment.navGraphViewModels(R.id.feature_nested_graph)
or, if are injecting into the ViewModel and you are using a custom factory for that:
val viewModel: SharedViewModel
by fragment.navGraphViewModels(R.id.feature_nested_graph) { factory2.create(assessmentId) }
You have the same instance of a shared ViewModel, because it belongs to the Activity - that's clear. I don't know exactly your use case, but usually when I need to do similar, I simply notify the ViewModel from Fragment's onCreate or orCreateView passing some identifier. In your case it could be something like:
viewModel.onNavigatedTo("fragment1")
This way the shared view model can differentiate what fragment currently uses it and refresh the state accordingly.

How can I create common ViewModel for two fragments with a dagger2?

I use Single Activity Architecture in my application and I need to create a ViewModel common for two fragments using Dagger2. If I create ViewModel for Activity Scope, it will live longer than necessary. How can i implement this smartly? Or its ok to use Activity Scope?
From Android developers documentation, the approach to use Activity scope for sharing ViewModel between fragment is fine.
You can refer the document here Share data between fragments

Should DialogFragments have their own ViewModel or should they use the ViewModel of their containing Fragment?

I am new to android and i am not sure if it is a good practice to let DialogFragments use the ViewModel of their containing Fragment, or should it have a ViewModel of its own.
I am asking this since Google recommends that each Activity/Fragment has its own ViewModel.
I don't see a problem with the dialog having it's own ViewModel. This would fall into the Single Responsibility Principle, where the Fragment only handles what's relevant to it and ditto for the DialogFragment. Try and keep them uncoupled.
https://en.wikipedia.org/wiki/Single_responsibility_principle

Should we Use ViewModels to communicate between 2 different fragments or bundles in single activity App Architecture?

Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.
Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.
Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.
You can use ViewModels to communication between two different fragments(aka SharedViewmodels) because it's simple but it's not perfect.
As you know the SharedViewModels must be alive until the first joint parent (activity or fragment) is alive.
but wait ...
What is the purpose of ViewModels?
Are we create ViewModels just for communication between fragments? Absolutely not.
Is the use of ViewModels against the purpose of ViewModels? no, I say it's not perfect use them for communication between fragments but if you have a small project you can use them because it's simple.
So what can I do instead of using ViewModels for communication between fragments? You can build independent UI components and use them for communication between fragments.
What is the advantage of building this weird independent UI components? In this way, each component has its own ViewModel (or Presenter) and they haven't any parent/child relation; Instead, they updated from the same business logic or in reactive programming they are just observing the same Model.
What is the meaning of building independent UI components and how to build them? if you are already familiar with reactive programming, I recommend reading this amazing blog post by Hannes Dorfmann; If you don't, I simply propose using EventBus library for communication between fragments but You will soon realize that too much usage of this library leads to spaghetti code.
Scenarios
If the fragments are not part of a flow/group, then don't share the ViewModel, just pass some id/data to the new fragment, create its own viewmodel, and query the data for the fragment from its own viewmodel.
If the fragments are part of some flow/group (cart/checkout/booking flow, multi-screen registration process, viewpager fragments, etc) and the logic is common enough, then share the viewmodels between the fragments. In single-activity architecture, I put these flow/process in its own root parent fragment that acts as a host and is used to create the scope of the viewmodel. For example:
MainActivity ->
-> RootAuthFragment
-> SplashFragment (replace with below)
-> LoginFragment (add to backstack with below or onsuccess login go to MainFragment)
-> SignupFragment (onsuccess go to Main)
-> MainFragment (replace with RootAuthFragment)
In the above scenario, you can share the viewmodel between login and signup screens with RootAuthFragment's scope. If you have a multi-screen signup process, then you could move that into separate root fragment and create a separate viewmodel for the signup flow.
Bundle vs ViewModels:
Bundles are used to pass some values. So, use it just for that. I use bundles to usually pass primitive data types or enums and based on that I query the actual data from the viewmodel (through android room or retrofit) or if the data objects are small enough, I make them parcelable and just pass that.
If you have a shared ViewModel and it's becoming a god class and does a lot of different things, then it means those fragments need separate ViewModels. Don't share the ViewModel just for data. Share the ViewModel for the common/shared behaviour/data/logic (whichever makes sense for your particular use cases)
I prefer you should use View Models approach if you are using single activity architecture. To justify my answer I will clear your scenarios here.
Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.
Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.
As for memory you are already holding information into memory there is no escaping there. If you don't need data for stay there then you can clear data from models also but again it will kill the purpose of storing data in the first place.
If you pass data using bundle it's also going to take memory there also.
Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.
That's the main purpose of using view models it will store the last state for user where he left.
As per https://developer.android.com/topic/libraries/architecture/viewmodel states
This approach offers the following benefits:
The activity does not need to do anything, or know anything about this communication.
Fragments don't need to know about each other besides the SharedViewModel contract. If one of the fragments disappears, the other one keeps working as usual.
Each fragment has its own lifecycle, and is not affected by the lifecycle of the other one. If one fragment replaces the other one, the UI continues to work without any problems.

Categories

Resources