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.
Related
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.
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.
I have been tasked with remaking an entire app basically at work. Essentially the app is just a feed that contains multiple types of cards (think facebook with update cards, photo cards, OG content cards) etc. I moved all of the network handling into a single class. You call the method that contains the request you want, passing in relative parameters and a Listener. The network call is made and then the Listener is passed the response.
Currently I have a Fragment class that populates a recyclerview with my custom adapter. The Fragment class interacts with my network class by making pagination requests on an endpoint. Should I have an adapter listener that communicates with my fragment when for example, the like icon on a particular viewHolder card is clicked? Should I then have the fragment make and manage that network call? The issue I have to account for here is if a network response fails, I need to unset UI elements (like the color of a like icon) to notify the user that their request could not be completed at this time. Unsetting these elements are properties of the view holder at that position in the recycler view. It seems wrong to pass all of this view holder information back to the fragment just to have the fragment be the only class that interacts with my network class. Is there anything inherently wrong with having my adapter handle network requests as well.
For example, Set an onClickListener on my view holder element, when clicked update the UI and make the call, then respond back to the adapter. If successful leave everything be, if failure, then unset the UI. This pattern makes sense to me and decouples individual view logic from the fragment. Is this fine to do? A lot of google searching has lead me to believe otherwise. This is my first business application and I want to make sure I follow the best patterns possible.
My fragment for the record looks something like this
onCreate() {
networkListener = new NetworkResponseListener() {
//Process success response based on response code
onSuccess(JSONObject response, responseCode)
onError(Error e, responseCode) // Handle Errors
}
}
//After scroll listener indicates I need to fetch again
NetworkHelper.makeFeedFetch(networkListener, LastId, GET_FEED_REQUEST_CODE);
So essentially I would do the same type of Network interaction in my adapter class for actions like, viewHolder like icon clicked and so on. Is that acceptable?
If you really want to make sure you follow the best patterns, you shouldn't make NetworkCalls from your fragment. Fragments or Activities should be just viewed objects that show the data that is served to them by other classes like Presenters(if you follow MVP) or ViewModels(MVVM). These classes also do not make direct network calls.
Generally, we use the Repository pattern which gets data from the remote(web services, remote DBs etc.) or local sources(a local DB).
Your network class is a candidate for a repository source. Then for each action
that is necessary for our business logic, we generate a use case, and if this use case needs the repository we inject our repository to our use case. Then we inject this use case to our Presenter or ViewModel. This much of indirection may seem like overkill but it gives easily extensible and testable code. Recycler view is also a view so you shouldn't make network calls
inside of recycler view too.
Although not an exact solution, your
Set an onClickListener on my view holder element, when clicked update the UI and make the call, then respond back to the adapter
is better IMO.
As a background, I am using MVP mosby for my android application.
Currently I have this UI design requirement, where from almost everywhere across the app. (4 different activity/fragment/recycler adapter). If the user taps on an item, I should present a dialog (same everywhere) and the dialog itself needs to make API calls, and need to handle any error that comes back.
I wrote this the present dialog logic inside a helper class.
#EBean
public class DialogService {
Dialog d;
public void presentUniversalDialog(Context context, Data data) {
d = new Dialog(context);
.. set view
.. change some text view based on data
.. make some api calls
}
private void makeAPiCall() {
.. some API calls here. On Return, update the dialog d if not null
}
}
So then in my other activities, I just need to inject this service and I can easily show the dialog by calling
#EActivity
public class MyRandomActivity extends Activity {
#Bean
DialogService dialogService;
#Click(R.id.my_random_button)
void onButtonClick() {
dialogService.presentUniversalDialog(this, data);
}
}
Now, the good news is that all the random activities should not be bothered by this dialog as long as it is launched. So I don't need to pass random event listeners around.
But how do I structure my dialogService code to deal with async events?
For example, the "data" field might contain only an id so I need to make API calls to populate the whole data. And once user clicks on OK. I need to send a request to confirm.
For now, I worked around by basically keeping track of the API calls via some member fields inside the DialogService. But as code gets large, this will quickly fill up and starts to be super confusing.
What is a recommended way of writing this "universal dialog"? Should I perhaps only use service per dialog? Or are there some other ways?
Treat dialog as View (in MVP) and give it its own presenter as gateway to your business logic (to make http request). So just treat Dialog not different than you would treat a Activity or Fragment in MVP.
Also worthwhile checking DialogFragment
I'm still figuring out RxJava and using it to do some networking stuff with Retrofit 2. Been trying it our for a a couple days and like that the code looks more readable now but have come across an issue that I cant seem to figure a way around.
I am trying to perform a login (which returns an API token) and then use this token to fetch some initial data all in the same chain so that the output of the chain is the token + data. To do this I call my API service with a
apiClient
.login()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(token -> getData(token))
.subscribe(new Subscrber<Bundle>() {...});
This seemed fine, but I also wanted to show a progress bar when starting and stopping the chain. So I added a .doOnSubscribe() and a .doOnUnsubscribe() to this as well. However I noticed that after orientation change the fragment that I was trying to hide the progress bar is always null.
So I searched and came across the RxLifecycle lib that seemed like it would help and I now .cache() and unsubscribe from the event chain. But I cant figure out how to subscribe to the same event again in onCreate() after this? I think I'm missing something pretty basic and would appreciate any help with this.
You don't necessarily have to use any architecture pattern to accomplish that. Though any MVP/MVC are nice things for concerns separation, testing etc, making your Controller/Presenter/DAO an application-wide singleton, which keeps up memory through whole application's life is not exactly a good idea.
Here's a sample project using retained fragment instance and RxJava - https://github.com/krpiotrek/RetainFragmentSample
The main idea there is to use Fragment with setRetainInstance(true) called, which protects it from being destroyed on orientation change, and store your Observable in there. Here's how you handle that in Activity/Fragment onCreate
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
// first run, create observable
mInfoObservable = createInfoObservable();
// set Observable in retained fragment
RetainFragmentHelper.setObject(this, getSupportFragmentManager(), mInfoObservable);
} else {
// following runs, get observable from retained fragment
mInfoObservable = RetainFragmentHelper.getObjectOrNull(this, getSupportFragmentManager());
}
// subscribe
mInfoObservable.subscribe(...);
}
Keep in mind that your Observable has to cache last value, one way is to use cache() operator.
You need to make sure that you subscribe to the same Observable instance that is returned from .cache(). Typically you would store this instance somewhere in a Singleton (like the Application class), a retained fragment or an Android Service.