Consider we have some Disposable in our RecyclerView.Adapter and we added them to a CompositeDisposable.
which Adapter method callback is the best choice to clear() the CompositeDisposable ?
Currently I did on the onDetachedFromRecyclerView. I want to be sure about how correct is this.
It would be easier to answer if you could provide the code of the adapter.
In general, Disposable should be disposed with respect to your business logic and the component containing lifecycle.
I would also say that it is better to not use Rx inside of RecyclerView adapter. Here are the benefits:
Adapter logic remains simple and synchronous.
You don't need to think about possible lifecycle or multithreading issues when developing the adapter.
Rx streams always allocate a lot of memory, so (if we talk about RecyclerView) putting them into the wrong place can lead to performance issues.
So, I'd suggest moving Rx streams into Presenter/ViewModel or similar component.
Related
I'm trying out StateFlow in DataBindings and in all examples I can find, most look like a copy of this one, two variables are used in the ViewModel for one data binding. One private MutableStateFlow and a public StateFlow. And the StateFlow is pretty much just reading the value from the MutableStateFlow. Why is this? Wouldn't it be easier to just have one variable, a MutableStateFlow and skip the StateFlow variable all together?
This is just a simple case of encapsulation (and is unrelated to data binding/Android). From a technical standpoint, this is a redundant step and doesn't make a difference. From a design standpoint however, it restricts modification of the StateFlow value outside of your containing class.
This is quite a common pattern with 'observable data holders' (MutableLiveData and LiveData), which can be thought as similar to having a property with a private setter. In fact, if you only need to collect the flow and not access its value, you could even use Flow as the exposed type (although as #MarkKeen pointed out, this won't work with data binding).
From official we know
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way
But i think lot of developers use ViewModel as both data store and controller(like calling repository, network client for data). I also use as for both data store and controller for view.
Android official sample code has also some controller logic. From official :
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
Here loadUsers may calling some Repository or NetworkClient . So here it acting like controller.
I am sure many developer do this way, but as from definition ViewModel should store and manage UI related data, should ViewModel act as a Controller ?
I found some stackoverflow thread this and this about this.
First one accepted answer suggested not to use use ViewModel as Controller and to use a Controller for other task.
In comment section of Second one #commonsware also suggested not to use complicated things other than data.
So my question is
What will be the actual responsibility of ViewModel from architectural concept?
If i have to do some method calls related to View [like data query, network call and other business login related stuff ] where should i do it?
and if i have to use a Controller then how i connect View and Controller for device rotation and sharing controller between Fragment ?
Hope my question is clear to all
Thanks in advance.
Here loadUsers() may calling some Repository or NetworkClient . So here it acting like controller.
I am sure many developer do this way, but as from definition ViewModel should store and manage UI related data, should ViewModel act as a Controller ?
Theoretically, the retrieval of data should be internal to the LiveData, triggered by having active observers and based on that, deciding what to do (in onActive()). If the LiveData is actually a MediatorLiveData, then this also applies to any block bound with addSource, as the block added with addSource of a MediatorLiveData is only called when the MediatorLiveData is observed by an active observer
You can see this technique used to its fullest in the NetworkBoundResource. The ViewModel only stores data, and knows nothing of data loading.
What will be the actual responsibility of ViewModel from architectural concept?
If you see the comments by Yigit Boyar (creator of ViewModel):
I'm the guy (or part of the team) that added it and it had nothing to do w/ MVVM. It is all about trying to give a class to people where they should put the data.
AAC is not an MVVM implementation, nor VM concept only lives as part of MVVM.
In fact, the main motivation for this was; we've been telling devs not to manage data in the UI controller and the answers was also, so where? And ViewModel became that answer.
We want it to be the model for your view layer (fragment, activity whatever). On the hindsight, it could be better to pick a name that is new but naming is really hard.
In conclusion: ViewModel is the Model in an MVC scenario, where C is the Activity or Fragment, V is the inflated view, and M is the ViewModel.
If i have to do some method calls related to View [like data query, network call and other business login related stuff ] where should i do it?
ViewModel gets the data in the form of a LiveData, and the LiveData is "activated" by observing it from the View with a given lifecycle.
Network calls are supposed to be also triggered in the same manner (if you follow the approach as per LiveData was designed).
In theory, if you have a login call, you could as well do it in the controller instead of the model, so you could do it in the Fragment, even though there are tricks like Jetpack Databinding that would let you call methods from the View on the Model directly from the XML.
and if i have to use a Controller then how i connect View and Controller for device rotation and sharing controller between Fragment ?
ViewModel exposes LiveData and can potentially also expose LiveEvent if you write the necessary code for that (unfortunately that is not provided by the Jetpack team, and neither are Command bindings), and either the View or the Controller can call methods directly on it if necessary. ViewModel is stored across config changes (not across process death, ofc) so it should not hold a direct view reference.
I have one Activity and i have created one View-model for it. I have created different classes like
UiUtil( show, hide view, hide key board ), Network layer , Data Base layer, AppUtil( for common functionality like Collection check, String validation, Date Conversion etc)
My question is, In MVVM design pattern is Activity can use these utility classes directly or it needs to use these classes via View-model, if it via view model then in the view-model i have to write a method that just call utility classes method . like below TimeDateManager is utility class used in view-model
class HomeViewModel: BaseViewModel()
{
fun prepareTimeAmPmToDisplay(context: Context, alarm: Alarm): String
{
return TimeDateManager.prepareTimeAmPmToDisplay(context, alarm)
}
}
Architectures are not obligatory, they are recommendational, thus you can change their usage in quite wide range. The only stopper should be a common sense(if it is present of course).
In this particular case the usage of utility class inside an Activity maybe ok, based on your ViewModel construction and its way of communication with View(read Activity).
For example if you have some LiveData that sends some kind of event(for ex. data loaded from backend or alarm trigger) inside your ViewModel and your View listens to it, I think it is ok to use util classes inside an Observer in Activity. Especially if this utils method doesn't depend on any ViewModel or Repository data. The direct utils usage in Activity is not limited by this usecase, though - there are plenty of others.
I understand that this may be an unpopular opinion in modern time of "clean approach" but I believe that this "clean approach" sometimes complicates stuff where it shouldn't, thus if mixing things a bit does not brake overall architecture but rather makes some thing more readable and easy to maintain - I would go for it.
Hope it helps.
My approach toward MVVM is simple, ViewModel is responsible for business logic, dealing with repositories (Network, Database, etc.) and all of the non-UI codes preparing the required data for UI, just like the documentation:
A ViewModel object provides the data for a specific UI component, such as a fragment or activity, and contains data-handling business logic to communicate with the model. For example, the ViewModel can call other components to load the data, and it can forward user requests to modify the data. The ViewModel doesn't know about UI components, so it isn't affected by configuration changes, such as recreating an activity when rotating the device.
On the other hand, ViewModels should not store a context (ApplicationContext is exceptional) and it's preferred that they do not use android APIs at all, so they become more testable (especially in the case on pure unit tests).
Also we are recommended to make use of LiveData in ViewModels and the UI has to observe the LiveData. For example, in onCreate of your Activity, you will call loadMainContent() method from VM, it calls getMainContent(page=1) from repository, and the repository will decide to load data from DB or network, and the result will be set on a LiveData were the View is listening for changes.
TL;DR
Sometimes it's even better to call these utilities from View rather than the VM. I'm pretty sure about your UiUtil also I think TimeDateManager is more view related rather than logic related. In addition, Network and DB layers are more efficient if called through a repository (which is responsible for caching, etc.) and VM can use that repo.
Most of the MVVM examples are dealing with very simple user interfaces.
But lets say I have an activity with many views to update (i.e. lots of data)
As I read in other places, multiple ViewModel objects is a bad pattern.
So, as I see it there are two solutions for that:
Create a single object (and single LiveData for it), that wraps all other data objects.
But there's a problem with this - each data object that gets updated will cause the whole UI to update.
Create multiple objects (and multiple LiveData objects for it).
It means that I need to observe each LiveData object. Is there a problem with this pattern?
Thanks in Advance!
First Point you mentioned : Yes this is not optimal Pattern to do but if you have small data then, separating LiveDatas is more work for less gains
Second Point you mentioned : Yes this is more optimal, you can have a LiveData object for each View you want to update and observe them all from your activity or fragment. There are no issues in this Pattern.
About Mutilple ViewModels :
Multiple ViewModels Pattern in same Activty/Fragment is also an option if you have too many things(LiveData objects or funcitions) happening in one ViewModel. This is only recommended to make viewModels lighter. So only use this if you are having a large viewModel class
Create ViewModels for discrete types of information.
You could for example have a UserViewModel that deals with all state regarding a User. This means you can use the same ViewModel in another context, without pulling data that might not be necessary (as you would if you had a single God ViewModel).
Create as many LiveData objects as you need to model your view.
It is better to condense the data into logical objects, where possible. If only to keep things manageable.
If you have a User, you should use that for your LiveData instead of having a LiveData for E-mail address, Display name, Age, etc. That will make things much simpler for your data bindings. Try to keep things logically grouped together.
LiveData seems very useful since it only notifies the view when the view is in an active state. It also stores and returns the last value to new subscribers right after subscribe.
My question is how to achieve the same thing with only RxJava?
Since Rx is a fully functional reactive solution combining it with another reactive solution doesn't seem right. I prefer if I could just remove LiveData from the project.
I know about https://github.com/trello/RxLifecycle and https://github.com/uber/AutoDispose but what they do is unsubscribing from the stream. I don't want that. I want my stream to exist as longs as view model is alive but my activities to start and stop listening to the steam base on lifecycle.
any suggestion will be appreciated
You can definitely do the whole thing with only RxJava. That's what I did with my team in many apps:
Regarding life-cycle management, we do two things:
We use the ViewModel from the Architecture Components to retain the ViewModel's states when the View gets destroyed https://developer.android.com/topic/libraries/architecture/viewmodel
We subscribe to our ViewModel's RxJava-Properties in our View/Fragment and dispose the subscription e.g. in onStop/onDestroy. RxLifecycle as you mentioned is actually of great help there. You cannot or rather should not retain the subscriptions when the View gets destroyed. It creates a memory leak since the subscription still has a hard reference on your View/Fragment. Also, the whole thing will crash if your ViewModel's RxProperties fire when the View was destroyed (e.g. when a server request returns after closing the app). So you need to dispose your subscriptions, no way around that in Android.
As #Samuel mentioned a BehaviourSubject/PublishSubject (depending on the case) is a great fit to provide Inputs to a ViewModel. The Outputs will be Observables which you subscribe to. Your ViewModel could have an interface with Outputs and Inputs like this:
interface TasksViewModel {
// inputs
Observer<Task> taskAddedTrigger();
Observer<Task> taskClickedTrigger();
Observer<Task> taskCompletedTrigger();
// outputs
Observable<Boolean> isLoading();
Observable<List<Task>> tasks();
}
Your ViewModel then just uses RxJava to map inputs to outputs in a very functional style. You Fragment supplies Inputs to the ViewModel whenever User input is received. It subscribes to Outputs and updates the user interface accordingly when the ViewModel's Output changes.
Here is a compact article where I summarize this whole Architecture (MVVM+RxJava)
Let me know if you run into trouble with any details of this approach. I used this architecture in many apps.
You can go with 2 solutions:
first one you can use LiveDataReactiveStreams to convert from a livedata stream to a rx stream and vice versa.
second one you may definitely get rid of livedata and use BehaviourSubject (a rx stream that store last value as live data), but as you said you'll have to subscibe/unsubscribe when your view resume/pause