I'm trying to implement the recommended architecture by Google and in a tutorial they show this diagram:
So I have a MainActivity and when the app starts it should go and fetch some data from the internet. I do those network operations in the Repository. Now my problem is that I don't know how to communicate properly between activities and Repository. For example MainActivity starts and immediately display a circular progress bar while Repository fetches the data. How can I stop the animation in MainActivity as soon as the data is inserted to the database? I guess I could call observe() on the LiveData and wait for onChanged(). Is there a better approach? What if there is no new data? Then onChanged() wouldn't be called...
Maybe I could send intent from Repository to MainActivity when there is no data so MainActivity knows it should stop the animation and if it doesn't receive the intent it just waits for onChanged()?
I guess I just don't feel confortable with the onChanged() method because I will never be sure of the operation it corresponds to. Maybe before the data from the network arrived there was some other data inserted which trigged onChanged() which would then stop the loading animation before it was supposed to.
Regarding your issue in the comments, which I believe to answer your main question also.
You need to observe from your UI (Activity / Fragment) to a progress LiveData in your ViewModel. That could be working with a Boolean (LiveData<Boolean>). To represent the progress view being visible or not.
That in turn needs to take an identical LiveData from the Repository (declared in the Repository as a MutableLiveData). You then post updates to the progress MutableLiveData in the Repository.
Now, whenever the MutableLiveData receives a change, that exists in your ViewModel as it shares the variable reference, and it will pass to the observer in your UI.
-
Alternatively, you could return a LiveData<Boolean> from the method in your Repository that pulls data. That would then be observable in your UI.
Instead of Boolean, you could also use a more complicated structure containing more information. A message, error code, etc.
Related
I have FragmentA and FragmentAViewModel. FragmentAViewModel calls RepositoryA for getContactData(), which is a silent call without blocking the UI.
Now User Navigate to FragmentB which has FragmentBViewModel. User needs the getContactData() response here in FragmentB.
I want to show loading progress in FragmentB until the data is available in RepositoryA. Once Data is avaiable in RepositoryA, i want to update the FragmentBViewModel and FragmentB .
How can do that.
NOTE:
I dont want to share the ViewModel since this is the small use case in whole feature.
I cannot use LiveData in Repository since i need Lifecycle owner to observe that.
Do i need to use RXAndroid or Broadcast for this ?
The flow is a bit strange to me but it's totally doable.
If both pages are reference to the same data source, ex: same database via Room interface.
Then first page can trigger the fetch or update while the second page subscribe for the update and display it.
I want to initialize the LiveData value when the app is launched, not every time the orientation changes.
Can I use the constructor of the subclass of ViewModel for it?
Instead of LiveData which pushes the last value to observers for every config change (like a RxJava BehaviourSubject), you should use something which pushes the event once.
You can use:
SingleLiveEvent: Send the event to only 1 observer, check here and here or alternatives here
LiveEvent: Send the event to all the observers, check here
Both of those approaches will not cache events, which means that an observer should be already observing the *LiveEvent to receive it
You can find articles online about alternative approaches but the philosophy behind them is probably the same
I am using MVVM pattern with repository for network call.
When i click button in my view it triggers save/ fetch data method in my viewmodel which then make call to repository wherein i am doing my network operation using Retrofit.
Now i would like to dismiss my progress bar with proper message (like saved, error etc. from my repository (onResponse or onFailure) that i started inside view.
public void onClick(View v) {
ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.setMessage("saving...");
dialog.show();
cuttingUnitViewModel.saveProjectUnit();
}
Is it ok to do what i am doing or is there better alternative?
All i want to say that I want to return NETWORK STATE and DATA both from repository , how to do that?
Main issue which I think in your case is that you initiate a call and get its response but your activity doesn't get to know any of these changes. Without this you might have to create some custom util classes or some other workaround which in my opinion would be a really bad practice.
My suggestion is that you create a MutableLiveData object for your response in your activity's ViewModel, then pass this object to your repository so that when your retrofit completes your call you can post the result on your livedata object.
And in your activity you should be observing this livedata for changes. So, when you get the result and post the new value, then in activity livedata observer's onChanged() you can get the result and even make your progress bar disappear.
Sorry, if this was too hard to understand. Here is a nice little tutorial you can refer to. I suppose that would make things easier with an example. Good luck!
No, it is not proper to do View activities like dismissing ProgressBar in the Repository as this class is only meant to deal with Data from different sources and this creates tight coupling. A proper design would be to set callback functions back to the class where the View is present from the Repository class through the intermediate layers like ViewModel until it reaches the View layer and then dismiss the ProgressBar there.
I am working on an application where there is a log in form.I am bit confused with the pattern as I don't understand as how I will open the new activity as my login is successful.As per my understanding when I click on submit button a method in viewmodel which authenticates will get call and after my successful login I do not know how to navigate it to activity file so that I can call new activity.
Thumb Rule:
No package from android.* should lie in ViewModel. You can ignore package import for ViewModel
Also, you can do it with normal ViewModel as well.
How to proceed?
Lets make it simple. Suppose, you are making Login page.
Lets have below things in ViewModel itself:
Fields of email & password with two-way binding
Form Validation logic
Button Click Event
Api Call
All these things lie in your ViewModel.
Now, your Activity needs to react to the result of your Api Call. So, let your ViewModel have a separate LiveData where T is the type of Response from your Api Call.
For example:
val loginApiStatus = MutableLiveData<LoginResponse>()
And then, let your Activity observe this LiveData. It should be observed in onStart() method of Activity. I will tell you the reason why to observe in onStart().
viewModel.loginApiStatus.observe(this, Observer{ loginResponse->
// respond accordingly
})
Now, once you receive response from Api, simply update the LiveData in your ViewModel as:
loginApiStatus.value = loginResponse // Login Api Response
With this structure, you have total control over handling the Api Response. Even if, your activity goes into background, after launching Api Call, you will still have the state of what happened to that Api call. Now, when you return to Login screen again from background, you start observing the LiveData again (because we are observing the state LiveData in onStart() as I said earlier), and you will get the state to react upon.
Life becomes a lot easier when you start storing states of your View / Fragment / Activity, in your ViewModel itself.
for that, you can use AndroidViewModel which gives Application Context and then using intent you can navigate to the new activity.
You can simply implement a click listener in activity and handle opening new activity from there. As far as I know, ViewModel should only be used to persist data or do other communication with Repository/model. It should not be used for navigation.
Although if you want to use then you can use AndroidViewModel class which provides a context object that can be used for navigating to another activity/fragment.
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