The problem is quite straightforward. The question is in context of using ViewModels, LiveData and other related Lifecycle aware arch approaches.
I have an Activity with NavDrawer, which switches fragments inside. And also I have a case when two fragments are present at the same time on the screen - this will be the main pain.
One Fragment has a ViewPager with nested Fragments(don't ask why).
The other fragment is just obtaining info from first one when user performs some actions. This is achieved just by sharing activity viewmodel. But the app itself has a lot of business logic and as it goes further the viewmodel goes bigger and bigger.
What I want to ask - not a receipt or rules how to fix this, or maybe how to overcome this by fixing the entire structure of the project. I want to ask for suggestions how can I apply the MVVM approach within android.arch.lifecycle style to mine use-case.
I haven't seen something more complicated then just sharing the Activity ViewModel between Fragments. But common, that's not a cure.
What you can see here - a mess actually. The point is that all are sharing the ActivityViewModel. Connections(aggregation) from FirstFragment mean that ViewPager inside FirstFragment is initiating ChildFragments and they are also working with the same ActivityViewModel(kill me). So as result everyone is working with one shared ViewModel.
My proposal is to add a ViewModel for each Layer. So that Activity/Fragments/ChildFragments have their own ViewModels.
But what appears here - how we should communicate then?
Possible solutions :
Having two ViewModels per one component. One ViewModel will handle/delegate the business logic and another will make the communication. Two viewmodels per component - not so good, yeah?
Having old manner interface(please no!)
Other workarounds - like DB/SharedPrefs/Realm change listeners and Event Buses(I'm too old for this :( ).
Your solution here!
I'll say that all of the above are breaking a lot of design principles, so what should I do?
How should I come out of this mess? Is there any Uncle Bob or another superhero here to help?
P.S. - Well, creating UMLs or other charts isn't mine forte. Sorry for that.
P.P.S. - I'm aware of google samples.
What i would suggest you can do is handle two ViewModel for your entire use case.
Make one ViewModel
Let's say MyActivityViewModel to handle all logic related for activity level. So, if any fragment logic is directly related to your activity then share your ViewModel like below :
ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class); // Like this in fragment.
&
ViewModelProviders.of(this).get(MyActivityViewModel.class); // Like this in activity.
This will share common ViewModel between your activity and fragment.
Another ViewModel would go for FirstFragment in your case if you have to share logic between your ChildFragment :
Here you can share ViewModel let's say FragmentViewModel like below:
ViewModelProviders.of(this).get(FragmentViewModel.class); // Like this in FirstFragment which is having view pager.
&
ViewModelProviders.of(getParentFragment()).get(FragmentViewModel.class); // Like this in View pager fragments, getParentFragment() is First fragment in our case.
Although, we can still use our activity level MyActivityViewModel in our child fragments from FirstFragment like :
ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class);
First there is no harm in having multiple ViewModel's for a single View.
I would think about my ViewModel's like what kind of data is getting and manipulating, and group them in a way, that seems natural.
For your case, if the fragments and the activity's logic is very similar, I think you can go with a single ViewModel, but I would avoid that.
What I would do is break the activity's ViewModel into smaller parts and reuse the proper ViewModel's in my Fragments, so that I wouldn't have a God ViewModel, nor roughly the same code in different ViewModel's.
This is updated version of answer given by Jeel Vankhede. And also Kotlin implementation of the same.
Since ViewModelProviders is deprecated now we have to use ViewModelProvider.
Here is how you do it in Activity:
ViewModelProvider(this).get(MyActivityViewModel::class.java)
Here is how you do in Fragment:
ViewModelProvider(requireActivity()).get(MyActivityViewModel::class.java)
To solve the problem of FirstFragment sharing its view model with its child fragments, you can use this code to access the FirstFragmentViewModel from any of the child fragments:
// in ChildFragment1
val firstFragmentViewModel: FirstFragmentViewModel by viewModels(
{ requireParentFragment() }
)
Related
Google states to use SharedViewModel in case of communication between Fragments by scoping it to the activity.
In a Single Activity Application, this means that the activity will be littered with ViewModels which might not be needed anymore and they will stay there for the whole lifecycle. Considering some example like a extended sign up flow or something considering a few screens.
One way recommended was using a parent Fragment as scope, but that is only possible if there is a parent fragment. It can be just different Fragments.
I came up with the following solution, I want to know how viable or how bad is it and is there any better way around?
Considering I have two Fragments called ExampleOneFragment and ExampleTwoFragment for sake of simplicity and I want them to have a shared scope without actually scoping it to the activity. Let's say I want to update text view in ExampleOneFragment from ExampleTwoFragment so I create a SharedViewModel like this for both
For ExampleOneFragment it will be:
private val mSharedViewModel by lazy {
ViewModelProvider(this).get(SharedViewModel::class.java)
}
And For ExampleTwoFragment I came up with this:
private val mSharedViewModel by lazy {
ViewModelProvider(supportFragmentManager().findFragmentByTag(ExampleOneFragment.TAG) ?: this).get(SharedViewModel::class.java)
}
This seems to be working, but I don't know what kind of issues can this cause.
Some Other Solutions are which I found:
According to #mikhehc here We could actually create our very own ViewModelStore instead. This will allow us granular control to what scope the ViewModel have to exist. But I don't understand how to make it work for Fragments?
Secondly, is the hacky way of scoping it to the activity still, but clearing it out via dummy viewmodel by using same key which I found here
Could anyone guide me what is the right approach? I cannot shift to NavGraphs since it is an already up and running project and scoping to activity just feels wrong. Thanks.
This seems to be working, but I don't know what kind of issues can this cause.
This code will only work if:
An ExampleOneFragment is created first, always
It is added to the same FragmentManager that ExampleTwoFragment uses, via a tag of ExampleOneFragment.TAG, always
If either of those assumptions fail, you will wind up with separate viewmodel instances, because supportFragmentManager().findFragmentByTag(ExampleOneFragment.TAG) ?: this will resolve to this.
But I don't understand how to make it work for Fragments?
You would use it the way it is shown in that answer, or by anything else that accepts a ViewModelStoreOwner. In this line:
val viewModel = ViewModelProvider(myApp, viewModelFactory).get(CustomViewModel::class.java)
You would get myApp from your Fragment as:
val myApp = requireContext().application as MyApp
Personally, I think the solution that you pointed to is very risky. The ViewModelStore lives for the entire lifetime of the process and is never cleared. You will wind up sharing your viewmodel across everything, and everything done by that viewmodel is leaked. The concept of creating a custom ViewModelStoreOwner is fine, but you should be doing something to tie the scope of that owner to the relevant lifetime for the viewmodels that it stores. The answer tries to dance around that in its last paragraph; too many developers will ignore this and run into problems.
One way recommended was using a parent Fragment as scope, but that is only possible if there is a parent fragment. It can be just different Fragments.
Your app is not written in a way to make automatic ViewModelStoreOwner management available "out of the box", then.
In the end, you are looking for a ViewModelStoreOwner to be shared between these two fragments. In your first solution, you are trying to hack that by using the ViewModelStoreOwner from one of those fragments, which only works if you can reliably choose which fragment that is. In the solution you pointed to in that other answer, you are trying to hack that by intentionally leaking a ViewModelStoreOwner.
There may be other approaches that you could consider, depending on your circumstances. For example, there may be some option with your dependency inversion framework (Dagger/Hilt, Koin, etc.) to rig up a ViewModelStoreOwner that is tied to a specific pair of fragment instances.
While discovering the MVVM with Kotlin and Android, I'm facing a small problem related to the organization of one of my fragments.
Suppose I have an activity that hosts a fragment and after a navigation (with NavController) the activity host a new fragment, which has multiples subfragments (perhaps through a ViewPager). All of the 3 fragments (the parent & the 2 children) must display precise part of a data. Furthermore the second subfragment has a button that could change the data & this change must update the UI of all the fragments.
Firstly in my mind, I was thinking all the data will be stored inside the parentFragmentViewModel due to the fact that their will be useful for the 3 fragments, but that's where my problem appeared.
How the subfragments's viewModels could handle these data & update it?
My first thought seems to be incorrect, because if we read the viewModel doc, we can see "However ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects."
So, my subFragments's ViewModels can't observe the parent one. I was thinking about sharing the same viewModel between the 3 fragments but I don't know if it's a bad practice or not and I don't know how to do it the cleanest way possible.
How can I resolve my problem?
EDIT
After further research, I tried this solution https://stackoverflow.com/a/53819347/7861724
I created the viewModel inside the parentfragment. Once done, I get it inside my subfragment.
It currently work but I'm not sure if it's a good practice.
Why do you need to observe any lifecycle-aware component? You can create setters for MutableLiveData in your ViewModel if you need to update it from he newly created fragments, this doesn't mean that the ViewModel is observing changes.
val data: MutableLiveData<String>
fun updateData(val newData: String) {
data.value = newData
}
The fragments can actually listen to changes from the ViewModel, but that's fine because in the moment they are destroyed, the observers will also stop observing. That means that you can have your buttons and everything updated with no memory leaks.
I am working with Fragments in Android. I have two identical fragments FragA and FragB. Since they are identical: I need a method that says if I am on FragA and a textView in FragA changed or updates the same textView should change/update in FragB. Can anyone help me with creating a method that takes data from FragA changed about a particular View and passes it to FragB to update its View.
I have tried using interfaces and ViewModel for communicating with fragments, but I can't seem to figure out how to update two identical fragments when let's say one of them changes.
This is actually an easily approachable problem these days. With a ViewModel we can achieve this easily.
We can make a ViewModel where our MutableLiveData can live.
Our two (or more) fragments can observe the LiveData events and both update at the same time.
I could post some code but I really think this CodeLabs is exactly what you are looking for and can get you up to speed in just a few minutes.
Android Lifecycle Aware Components Codelab
You can easily do this with an external and awesome library, https://github.com/greenrobot/EventBus . This is a hidden gem for easy fragment to fragment communication in android. The docs are pretty self explanatory.
Please note: if you are using eventbus please remember to register eventbus only in the fragments OR classes you will be using it in, you do not have register an eventbus even in the class from where you are posting the event, else it will be giving you a compile time error. Also DO Not forget to unregister the eventbus using EventBus.getDefault().unRegister(this); or EventBus.getDefault().unregister(context);
in order to prevent memory leaks
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.
Firstly, I know that with Model View Presenter there are different implementations, and in my mind as long as you have the layers of abstraction clearly defined and doing their appointed roles then how you implement this pattern is open to interpretation. I have been implementing this pattern in quite a few apps where there was just one Activity. I've now started a new project that has multiple Activities and attached Fragments, including nested fragments (ViewPager).
I'm now trying to translate the MVP to this project and I've hit a concept wall and would like some guidance and insights.
So far I've created the above structure and started to do a 1 : 1 relationship with View & Presenter (regardless of Activity or Fragment). I feel that this is OK, however if for example I sent a request to do something from an Activity View to its Presenter, which returns a result to the Activity View how would I go about propagating the result i.e. update all the other Activities/Fragments that are currently not in a Paused() or Stop() state. I feel like in this case there should be a central Presenter that updates all necessary Activity and Fragment Views, but I'm not sure how to go about doing this.
Currently when each Activity and Fragment is created it creates a new instance of a Presenter class, passing in itself as a reference (the Activities and Fragments implement their own interfaces), which the presenter stores as a WeakReference and can invoke the relevant interface methods when returning a result.
According to the docs whenever Fragments want to communicate with one another and the attached Activity you should use a callback interface. With this in mind should I have one callback interface that the Activity implements and the Fragments callback to whenever they request something, so in essence only the Activity would have a Presenter and Model layer that the Fragments have to callback to in order to make various requests?
Sorry if this sounds a bit muddled, hopefully this is clear enough to understand what I want to achieve, and if I’m thinking along the right lines... or totally off the mark!
I think this is okay to have a presenter inside activity. Basically activity is like a controller, it should know about the presenter.
It would be wrong to put a presenter inside a fragment if activity or other fragment needs it too. You can put a presenter inside a fragment only if this presenter is designed specifically for fragment.
which the presenter stores as a WeakReference and can invoke the relevant interface methods when returning a result
Why do you need a WeakReference here? If you have 1:1 relationship then I assume your presenter does not have it's own lifecycle, meaning that it's lifecycle depends on either activity or fragment. There is no risk of having memory leaks because it's not a singleton, right? It should be safe to have a strong reference.
I'm not sure if I answered your question because it looks a bit broad to me. My point is that, fragments are just separated "parts" of activity and you should treat them as parts. If presenter belongs to this part only, then it should be inside. Otherwise it should be in activity. You are right about using an interface to access activity, this is a well-known design approach which Google uses in their examples.
Nope, no interface anymore. You either use RxJava Observables to update all the views as described here or some kind of Bus(Otto-deprecated or EventBus). And you will like it because they make interacting too easy.