I'm following this tutorial on using MVVM with Retrofit
https://medium.com/@ronkan26/viewmodel-using-retrofit-mvvm-architecture-f759a0291b49
where the user places MutableLiveData inside the Repository class:
public class MovieRepository {
private static final ApiInterface myInterface;
private final MutableLiveData<EntityMovieOutputs> listOfMovies = new MutableLiveData<>();
private static MovieRepository newsRepository;
public static MovieRepository getInstance(){
if (newsRepository == null){
newsRepository = new NewsRepository();
}
return movieRepository;
}
public MovieRepository(){
myInterface = RetrofitService.getInterface();
}
I'm building a simple app and what I noticed is my repository class is quickly being filled with a lot of MutableLiveData objects. Is this actually the correct way to implement MVVM, LiveData, and the Repository pattern?
Edit1:________________________________________________
I've created an AdminLiveData
object that just holds the LiveData and has getters.
But how would I get reference to the ViewModel
inside my AdminRepo
class so I can notify the LiveData
inside the ViewModel when the Retrofit Network call is complete?
private AdminService adminService;
public AdminRepo(Application application) {
BaseApplication baseApplication = (BaseApplication) application;
RetrofitClient client = baseApplication.getRetrofitClient();
adminService = client.getRetrofit().create(AdminService.class);
//AdminViewModel viewModel = (AdminViewModel) ....
// Not sure how to get reference to the viewmodel here so I can get the
// LiveData object and call postValue after the retrofit calls
}
public void getFirstPageMembers(int offset, int limit) {
adminService.getUsersPaginitation(offset, limit).enqueue(new Callback<List<UserInfo>>() {
@Override
public void onResponse(@NonNull Call<List<UserInfo>> call, @NonNull Response<List<UserInfo>> response) {
if (response.body() != null) {
//firstPageLiveData.postValue(response.body());
//Since I create the LiveData inside the ViewModel class
//instead, how do I get reference to the ViewModel's LiveData?
}
}
@Override
public void onFailure(@NonNull Call<List<UserInfo>> call, @NonNull Throwable t) {
//firstPageLiveData.postValue(null);
}
});
}
The AdminViewModel
:
public class AdminActivityViewModel extends AndroidViewModel {
private AdminRepo repo;
private AdminLiveData adminLiveData = new AdminLiveData();
public AdminActivityViewModel(@NonNull Application application) {
super(application);
repo = new AdminRepo(application);
}
How do I get reference to the AdminViewModel
from inside my AdminRepo
class?
Repository objects in android projects should be thought of as gateways to the outer world. Communication with persistence facilities(Network, SQLite, Shared Pref) takes place in this layer. Because of that incoming data aren't supposed to conform to the android environment. For example, a string date field in incoming DTO should be converted to a date object using local date-time, you might need to save data coming from server to local DB. Additionally, other data-related tasks can be performed in this layer like caching in memory.
ViewModels represent the data that is shown in the user interface. Data in this layer should be ready to be presented on the screen. For example, a view might require data coming from two different HTTP requests, you should merge incoming data in this layer. (Still, you can separate responsibilities further. If these two requests are part of a single task or purpose, you can do this operation in the use case layer. ) From the android perspective, view models have more responsibility like preventing data from being destroyed in configuration changes. In view models, it is recommended that data should be presented to the view layer by a LiveData. It is due to fact that LiveData keeps the last state of the data and it is aware of the view layer's lifecycle (If it is used properly).
First of all, the repository layer mustn't be aware of the existence of any view model so you should not keep a reference of a view model in the repository layer. There are some reasons for it,
Of course, we need some kind of reference to the view model to notify it when a request is completed but we should do this implicitly in a systematic way rather than a direct reference.
Callback: It is an old fashion way of sending data back to the view model. Using callbacks extensively in your codebase causes callback hell that is not wanted. Additionally, the structured canceling mechanism is hard to implement using callbacks.
LiveData: At first glance, it seems like a great fit for this purpose but it isn't. The reasons can be listed as
In your case, it is especially bad practice because HTTP requests should not change the state of your repository object. You use LiveData as a kind of cache but there is no such requirement for this so you should avoid this. Still, if you need to use LiveData in your repo, you should either pass a MutableLiveData to your request method as a parameter so you can post the response via this LiveData or return a LiveData in your request method.
RxJava: It is one of the options. It supports one-shot requests(Single), hot streams(Subject), and cold streams(Observable). It supports structured canceling in some way(CompositeDisposable). It has a stable API and has been used commonly for years. It also makes many different network operations easier such as parallel requests, sequential requests, data manipulation, thread switching, and more with its operators.
Coroutines: It is another option and, in my opinion, the best. Although its API is not completely stable, I have used it in many different projects and I haven't seen any problem. It supports one-shot requests (suspend functions), hot streams (Channels and Stateflow), and cold streams(Flow). It is really useful in projects requiring complex data flows. It is a built-in feature in Kotlin with all operators. It supports structured concurrency in a really elegant way. It has many different operator functions and it is easier to implement new operator functions compare to RxJava. It also has useful extension functions to use with ViewModel.
To sum up, the repository layer is a gateway for incoming data, this is the first place where you manipulate data to conform requirements of your application as I have mentioned above. The operations that could be done in this layer can be listed shortly as mapping incoming data, deciding where to fetch data (local or remote source), and caching. There are many options to pass data back to class requesting the data but RxJava and Coroutines are better than others as I have explained above. In my opinion, if you are not familiar with both of them, put your effort into Coroutines.
The solution you're looking for depends on how your app is designed. There are several things you can try out:
LiveData<ScreenState>
and you just have to map your entities.Additionally, you could use coroutines with retrofit as coroutines are recommended way now for handling background operations and have Kotlin support if you wanted to give it a try.
Also, these links might help you when exploring different architectures or solutions for handling your problem architecture-components-samples or architecture-samples (mostly using kotlin though).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With