I'm creating a sample app (Last-Mvvm) to learn (and possibly show) the usage of mvvm pattern, using Android data binding.
I have an activity with my ViewModel object inside. I have also a RecyclerView adapter, which contains an arraylist of items that are converted to another Viewmodel. I want to save the state of the list inside the adapter (for rotation changes).
So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
Also, there is another thing that isn't really clear.
Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
I will try to explain
First. So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
and
Second. Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
on example from article.
On the schema below you can see implementation of OnClickListener and OnLongClickListener for RecyclerView item.
doted lines is a links
solid lines is a method calls
How it works
ViewHolderWrapper works as ClickListener for root view of ViewHolder. Depending on implementation ViewHolderWrapper can be proxy for HolderClickObservable or depending on SelectionHelper, ViewHolderWrapper can manually highlight selected item.
SelectionHelper is responsible for saving selection state and notify SelectionObserver about changes.
Listener (Adapter in this case) is responsible for highlighting of selected items and for updates.
Summarizing
First. You can restore adapter state via activity with methods onSaveInstanceState() and onRestoreInstanceState().
Second. You need to create lightweight ViewHolder which only responsible for binding data to view. Actions can be performed at adapter or activity.
See also example application
Related
I have multiple Activities with the same CardView. To inflate this CardView I reuse the ViewHolder in my adapter. Now I'm trying to handle a click on the ViewHolder with setOnClickListener() and I need to save the Model represented by CardView into the database using a Repository. So for MVVM, the repository can communicate only with ViewModels and Databases or other resources, but every Activity has a different ViewModel so I couldn't pass the ViewModel to the Adapter to update the Model into the Database.
So I'm thinking that the adapter that I reuse, needs only one ViewModel for the database actions indifferently from the Activity that uses it.
So I need a static method in the main ViewModel that saves that Model.
This approach is correct for the MVVM pattern?
I don't know if I understand your question correctly but I guess that you want to somehow send a data from recyclerview adapter to your database.
"so I couldn't pass the ViewModel to the Adapter to update the Model into the Database"
You should never pass any viewModel to the adapter class because you will tightly couple them together and they wouldn't be reusable anymore.
In your case you should send data from adapter class and receive it in activity. Then activity should call viewModel to update specific data and viewModel will then call repository which will access database and save data.
It sounds complicated but it is well organised and you can reuse all components in many activities/fragments because they are not depending on each other. Adapter sends events that are received by your activity but the adapter does not know (and shouldn't know) which activity will receive the data. If you would pass viewModel as a parameter to the adapter class then it couldn't be used in activities that have different viewModel.
P.S Try to not use activity for all your views, in Android we have a Fragment component which is very useful in that case.
Useful links
fragments https://developer.android.com/guide/fragments
MVVM https://proandroiddev.com/understanding-mvvm-pattern-for-android-in-2021-98b155b37b54
This is the 'like' feature on Facebook.
I would like to synchronize these recyclerviews with these two pieces.
If you click on the 'Like' button on the recyclerview in one piece, the 'Like' button on the recyclerview should change when you change to another piece.
Which method should I use?
interface?
service?
Map Should I use this?
What method do you use to synchronize the data of two fragments?
You should be using ViewModel's from architecture components.
https://developer.android.com/topic/libraries/architecture/.
Basically you create a view model in the activity so that it is stored with the activity scope
//this is the instance of the activity
ViewModelProviders.of(this)
You can then get an instance of this view model in each fragment using
ViewModelProviders.of(getActivity())
The view model can then be used like in a standard MVVM architecture.
Each fragment should register to the lifecycle aware components that the ViewModel would provide. MutableLiveData is one such component that you could use to provide the data back to whoever is interested in the data (in this case each fragment)
Be aware that LiveData while does a fantastic job can be limited as it stores data as a state in time. This is great, but android should be developed where it is driven by events)
As an example If you have a viewmodel which sends data to the view via livedata it could trigger a dialog. When the user closes that dialog and causes a configuration change (destroys and recreates the activity) the view will receive the state of the live data at the point in time it was set which will again show the dialog. Basically each time you rotate the device it could show the dialog agian even though you've dismissed it.
A hacky fix to this is to notify the viewmodel to remove the state in the livedata after the dialog is dismissed. but this creates a number of other issues including tying view state with the viewmodel
It's a lot more flexible if the Lifecycle aware component instead sends events of when data changes. Think Rxjava that is lifecycle aware. You add the data to the RXJava component and the observable provides the data to the observer when the view is in a state to consume it (> onresume and < ondestory).
Hopefully that gives you a starting point. Let me know if you need more details
TL;DR
How do I deal with Activities that actively change data (for example through an EditText)? Do I keep saving their state in the SavedInstanceState on rotation and only use the ViewModel when all of the fields are ready, or is there a way to make the ViewModel responsible for checking/holding/using the UI's data?
Question
I'm developing my application using Google's Arch. Components, and writing my latest class I've noticed I'm not really sure on what the best practice is when handling, say, data coming from an Activity form.
Example
I have a POJO made of title, description, location, type
I have an Activity with four EditText: title_et, description_et, location_et, type_et.
My ViewModel, through a Repository (irrelevant here), can send an object to the Database when the sendObject function is called.
How I'm doing it now
The activity has the mTitle, mDescription, mLocation, mType.
On rotation, the activity saves all of the EditText values in the savedInstanceState bundle, and it loads them again populating the views.
When the user wants to send the object, it clicks a button and the activity calls the function viewModel.sendObject(mTitle, mDescription, mLocation, mType) after the necessary checks.
Problems with this approach
The activity is responsible of holding/checking all the data of the EditTexts, basically making the ViewModel only responsible of interacting with the Repository.
What I'd like to accomplish
Ideally, I'd want to make the Activity only responsible of the UI, delegating everything to the ViewModel.
This way I could call sendObject() and the ViewModel would have already all of the data needed.
The LiveData situation
Right now the ViewModel has only one instance of LiveData, inside that there is a Resource (which is taken from here) and it's used to "tell" the Activity that new data has arrived or an error occurred.
This approach works fine in all Activities that just receive data from the network and display them. What do I do when I want to synchronise data coming FROM the Activity? Do I use one LiveData for each field and use that to display potential errors?
I've read most of the samples but all of the Activities there are passive.
Conclusion
Thanks to anyone who takes the time to help.
You can either separate the logic into a model string class with another class containing all your String values for the edit text fields are just assign the String values at the top of your class.
You can have a LiveData of your model in the ViewModel and alter it from the View (Activity/UI). The downside is that to update the LiveData, you need to copy whole Model, edit it and post it back to live data.
The second way is to dissect Model's components in the ViewModel into individual parameter LiveDatas. Later when form is submitted you can reconstruct the Model.
What you can do for native fields is use data binding. For other you need manually update LiveData from the View with listeners etc.
I was wondering whether android binding is compatible with live data on conceptual level.
There is a simple task: call server after button is clicked.
So in my view I have
android:onClick="#{viewmodel::onOrderButtonClick}"
and proper onOrderButtonClick(View) method is defined in ViewModel.
But in order to make server call via LiveData I need my Fragment reference (observe() method needs LifecycleOwner instance as first parameter).
Of course I cannot hold reference to fragment in my ViewModel.
What is the pattern here? Do I really need to implement all event methods in the fragment class and delegate them back into view model class?
After some digging there is a bad news and a good one.
The bad news is that the fragment has to be used anyway (there is always some code in the fragment for each livedata event)
The good one is that it can be done relatively clean:
Call getOrderObservable() from fragment to view model. It returns
MutableLiveData<> created in view model's ctor.
Then call observe() on that observable In view model's onOrderButtonClick()
In onOrderButtonClick() in view model just call setValue()
That solution in my opinion minimalizes amount of code in the fragment. Still it looks not so elegant to separate making the network call and handling the result
Currently I'm working on project which is using RxJava together with RxBinding to observe views' changes. It's working really well for fragments and activities where we have easy access to life-cycle events - as it's recommended we bind to data streams on onResume and unbind on onPause.
Lately we've introduces RecyclerView, which display list of views and all of them can be data stream which we would like to subscribe to. The problem which I faced is passing CompositeSubscription object from activity/fragment through adapter down to view holders when they are created. Of course it doesn't work well ViewHolders won't be recreated when user leaves a screen and comes back (onPause and onResume are being called).
The solution would be to make adapter, layout manager (to access existing view holders) life cycle aware, but it require from us to write extra code to pass those subscriptions reference between logic layers.
However one of my colleagues proposed to use event bus, which would be used to pass Subscription in a event to activity/fragment, where they'll be added to CompositeSubscription and all of them will be unsubscribed all together. Moreover we can inform view holder to subsribe themself when user returns.
What do you think about this approach? Are there any pitfalls which I should be aware of in this approach?
Do not make your Views lifecycle aware. This violates separation of concerns.
Just use clickListeners upon viewBind in the Adapter.
Don't pass the Subscription to the adapter. (The adapter doesn't need to know about it, nor control it's lifecycle) The adapter could just offer an Rx-endpoint that you subscribe to in the (for example) Activity onStart and unsubscribe in onStop. Then Adapter can handle the marshalling of click events on items into an Observable.
Remember: You shouldn't apply RxJava to every problem.