Creating my own ViewModelStore to control ViewModel lifecycle - android

It is stated in google example that to communicate from fragment to fragment, you could use the ViewModel scoped to the Activity. The problem with this approach is that, then the ViewModel will last until the Activity is destroyed.
In a Single Activity Application, this means that the activity will be littered with ViewModels which might not be needed anymore. You will also have problem with states if these ViewModels won't be cleared properly.
So I look around at how to alter the lifecycle of the ViewModel so that I doesn't have to be tied to the Activity lifecycle but be longer than the lifecycle of a Fragment. This will then be very useful for Multi-step / Transactional flow of screens where the requirements are filled-up during the screen flow process.
So basically, I would want a ViewModel to be scoped less than the activity but longer than a fragment.
To accomplish this, I created my own ViewModelStore and persist it across configuration the same way FragmentActivity persist its own ViewModelStore. Then when initializing the view model I will use,
ViewModelProvider(myCustomViewModelStore, myFactory).get(SomeViewModelClass::class.java)
Since the ViewModel is not scoped to my custom ViewModelStore, I could easily call viewModelStore.clear() to control the lifecycle of the ViewModel.
I was wondering whether this is a good idea and whether someone out there is using the same idea.
Thanks in advance!

As of Navigation Component 2.1.0-aplha02, ViewModels can now be scoped to transaction flows through the Navigation Component navigation graph.

Related

What should be lifeCycleOwner of LiveData-Fragment or Activity?

Will Activity be lifeCycleowner of LiveData?
or
Will Fragment be lifeCycleowner of LiveData?
The answer is as per my understanding and I would say, it depends on where LiveData is being used or called from?
Let's go through some basics.
LifecycleOwner by definition means
A class that has an Android lifecycle. These events can be used by custom components to handle lifecycle changes without implementing any code inside the Activity or the Fragment.
Lifecycle
Lifecycle-aware components perform actions in response to a change in the lifecycle status of another component, such as activities and fragments. These components help you produce better-organized, and often lighter-weight code, that is easier to maintain.
Since the Lifecycle of Activity and Fragments are different.
Activity - Activity has its own lifecycle
Fragment - Each Fragment instance has its own lifecycle. To manage the lifecycle, Fragment implements LifecycleOwner.
Fragments are basically contained inside an Activity; so if the Activity is destroyed, Fragments will also be destroyed.
But it's not necessary that if fragments are destroyed, Activity will also be destroyed.
Live Data
You can register an observer paired with an object that implements the LifecycleOwner interface. This relationship allows the observer to be removed when the state of the corresponding Lifecycle object changes to DESTROYED.
Now coming back to your question.
when LiveData is observed in Activity, the lifecycle owner is Activity itself.
when LiveData is observed in Fragment, the lifecycle owner is Fragment itself.
What should be lifeCycleOwner of LiveData-Fragment or Activity?
It really depends on where you're using liveData. LiveData is lifecycle-aware, which means they respect the lifecycle of android app components.
If you are using liveData to observe data inside activity, then you should observe using activity.
If you are using liveData to observe data inside a fragment, then there is no point in using activity as lifecycleOwner. Using activity would lead to a memory leak, if fragment is replaced or removed, as liveData would still hold a strong reference to the fragment, which will be kept until activity is destroyed.
We should use viewLifecycleOwner inside fragments, as they are bind to the lifecycle of fragments.
As stated in the Google documentation
Each Fragment instance has its own lifecycle. When a user navigates and interacts with your app, your fragments transition through various states in their lifecycle as they are added, removed, and enter or exit the screen.
So each fragment and activity have their own lifecycle.
When you observer LiveData in the fragment or activity you pass the lifecycleowner associated with that fragment or activity.

Communicating between Activities and Fragments when using Navigation Architecture Component

I want to let the host Activity know when something happens in a Fragment. Traditionally, I would have an interface with a callback that the Fragment can call, but now we are ofc using the navigation architecture component.
Is there a way to pass a reference to the host activity down to the fragments or how would I otherwise solve the "Communication between activities and fragments" situation?
Thanks!
You can use the LiveData data holder class for such purposes.
Here is an article explaining both Fragment <--> Fragment communication and Activity <--> Fragment communication.
you can have a shared ViewModel between your activity and all of your fragments. and use liveData in that viewModel. so when something happens in the fragments you change the liveData and you observe the liveData in the activity
You can use a Shared ViewModel (https://developer.android.com/topic/libraries/architecture/viewmodel#sharing)
It would be alive while the activity is and all child fragments can access it.
A LiveData in a Shared ViewModel, for example, could be listened to in all the fragments. If one of those fragments change the data all others would get that change.

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.

Is it possible to call ViewModel methods from fragments through an Activity or is it a bad practice?

I have an Activity and 4 fragments in it. At first I wanted to do for each fragment of the ViewModel. But the situation is such that I also need ViewModel for Activity. I want to know if it would be an error to make the ViewModel just for the Activity and call the necessary methods from the fragments using getActivity? For example, call getActivity().myViewModel.callMethod() at fragment? Wouldn't this approach be wrong?
Yes, you can use the ViewModel of the Activity at a fragment, but not like that getActivity().myViewModel.callMethod(). It should be like this
YourViewModel viewModel = ViewModelProviders.of(getActivity()).get(YourViewModel.class);
And it's a good practice to share data between fragments.
Official doc of Google says
That way, when the fragments each get the ViewModelProvider, they
receive the same SharedViewModel instance, which is scoped to this
activity.
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.

How it impact on memory if I create all View Models with Activity scope?

I'm using new Android architectural components : MVVM, LiveData, etc. As recommended by Android documentation as well as in many blogs, in order to share data between fragments, we should create SharedViewModel which will be accessed by each fragment using ViewModelProviders with Activity scope.
I just want to know, Is there any impact on memory, if I create all my ViewModels with Activity scope?
Do they automatically destroyed on Android low memory?
No you should aware about memory issue.
In the navigation AAC, Google recommands single activity application.
But that is hard in some part.
So I use separated activity to using same view model scope.
If a, b, c must use same Viewmodel, just make a A Activity for them and separate with d, e fragment(in B Activity).
Using shared ViewModel is common when your fragments communicating with each other or should use the same data, Not for all separated fragments.
Using a single ViewModel for all fragments is a bad practice and further impact on memory, decrease code simplicity and makes your ViewModel to a God object that decides for all other components.
It's normal that each activity/fragment have its own ViewModel, and manage its own data and ViewModel lifecycle.

Categories

Resources