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
Related
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.
I would like to use a LiveData for handling kind of notifications, as it is already lifecycle aware, between a custom view and its wrapping fragment. But it seems that a LiveData may loose values : it will only update to its most recent state and also won't fire values during inactive state of its observers.
I've looked at the SingleLiveEvent purpose from Google code samples, but that solution does not seems to be battle tested yet, and the ticket is still open with recent tries to improve the solution.
So I am looking for a simple way to get notified about events, and at the same time not being worried about Lifecycles (that was why I went for LiveData as a first solution), and that could handle multiple observers.
Is there an existing solution for that ? If I try to implement it, it is sure that I will land into at least an anti-pattern.
One easy way (perhaps too easy) is to use callbacks : but the problem is that I need this feature for several callbacks in my component, leading me in a poor architecture. And also, I want a subscribe system, meaning that there could be more than one observer.
One other way, could be to use RxJava and tranform it into a LiveData, with LiveDataReactiveStreams.fromPublisher() : but now the question is whether I will get all values or only the last one. That's the closest solution I could deal with.
As an interesting alternative there could be AutoDispose or RxLifecycle. And an interesting resource I've found : Blog post on LiveData
What are your thoughts, suggestions ?
Also, please notice that I need this communication from a component wrapped into a Fragment (ChessBoard) toward another Fragment (ChessHistory). So they are both lifecycle aware.
It is not ideal, but this does the trick for me:
/**
* This LiveData will deliver values even when they are
* posted very quickly one after another.
*/
class ValueKeeperLiveData<T> : MutableLiveData<T>() {
private val queuedValues: Queue<T> = LinkedList<T>()
#Synchronized
override fun postValue(value: T) {
// We queue the value to ensure it is delivered
// even if several ones are posted right after.
// Then we call the base, which will eventually
// call setValue().
queuedValues.offer(value)
super.postValue(value)
}
#MainThread
#Synchronized
override fun setValue(value: T) {
// We first try to remove the value from the queue just
// in case this line was reached from postValue(),
// otherwise we will have it duplicated in the queue.
queuedValues.remove(value)
// We queue the new value and finally deliver the
// entire queue of values to the observers.
queuedValues.offer(value)
while (!queuedValues.isEmpty())
super.setValue(queuedValues.poll())
}
}
The main problem with this solution is that if the observers are inactive at the time the values are delivered via super.setValue(), then the values will be lost regardless. However, it solves the issue of losing values when several new ones are posted very quickly – which, in my opinion, is usually a bigger problem than losing values because your observer is inactive. After all, you can always do myLiveData.observeForever() from a non-lifecycle-aware object in order to receive all notifications.
Not sure this will be enough for you, but I hope it can help you or give you some ideas about how to implement your own approach.
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.
I have and Android app with the view class (Fragment, Activity) observing its ViewModel.
The ViewModel exposes methods such as getUserName which returns Observable<String>. Although maybe there is a possibility to find a better name (maybe observeUserName), I'm happy with the current one - it is quite explanatory.
However, here starts the hard part: ViewModel also can tell the view to perform some operation - for example close itself, pop backstack etc. For this case ViewModel defines following method (and corresponging Subject):
class ViewModel {
// other methods, fields
// ViewModel can call returnToPreviousScreen.onComplete()
CompletableSubject returnToPreviousScreen = CompletableSubject.create();
Completable returnToPreviousScreen() { return returnToPreviousScreen; }
}
In my opinion, the method's name is terrible. Hovewer I can not find anything better. Something like observeWhenToReturnToPreviousScreen is maybe more explanatory but hard to read.
So, are there any recommendations or maybe commonly used practices for naming such methods?
There's no universal answer to the naming problem, so the only thing you can get are opinions.
Rule of thumb
My approach to naming in rx-java usually looks at two things:
Does it express a "stream" of emitted events (usually with a plural form of a noun)?
Does it work well with other parts of rx java methods chain, and especially the subscribe method?
Both of the above can be usually simplified to trying to put the name of the method in this sentence:
This code subscribes to {name_of_the_method}.
Examples
A) getUserName
This code subscribes to getUserName.
👎 The sentence does not really make sense because getUserName does not express the stream. Quite on the contrary, it suggests that there is one value that you can get.
getUserName().subscribe()
B) observeUserName
This code subscribes to observeUserName.
👎 Although the method kind-of expresses the stream of events, it does not work well with subscribe. Method exposing the Observable is not a place for information about observing. The consumer of the method will be observing what that method returns.
observeUserName().subscribe()
C) userNames
This code subscribes to userNames.
👎👍 This might work in some cases. It nicely expresses a stream of userName items being emitted and works well with subscribe. It really depends on a particular scenario, because it suggests that you can expect multiple userNames while you really want to observe how a single userName changes.
userNames().subscribe()
C) userNameChanges
This code subscribes to userNameChanges.
👍 This method nicely expresses that there is a stream of items ("change" events) and it works well with subscribe method.
userNameChanges().subscribe()
Return to previous screen
As far as your returnToPreviousScreen case goes, I think I would end up using something like:
This code subscribes to returnRequests().
or
This code subscribes to previousScreenRequests().
or even a singular form as there can only be one event emitted in the stream:
This code subscribes to previousScreenRequest().
(not a topic of a question but I think I would use a Single<Unit> rather than Completable, to express a singular event emission rather than a completion... but maybe that's just me).