I want to do GET request with Retrofit, but I don't know on which layer of MVP pattern I need to do it. As I know, Model sends all data to Presenter, and then, Presenter shows data on View. So, I thought that the best place is Model. But how Presenter will know that Model fetched all data already and ready to pass it to Presenter? For this, I think I need to use interface that notifies Presenter when Model has finished loading data. But googling what is the best way, I saw that developers use something like Repositories and Managers. But I couldn't figure out the role of each of them. So, how to solve the problem? What is the best place to create HTTP requests in MVP pattern? If it is Model, what is the best way to send all data to Presenter?
What you need is a callback structure from you model to the presenter. What I normally use and recommend is to use RxJava, retrofit2 already has an option for returning an Observable object which makes everything much easier.
Let's say you have an endpoint like this one, this is a retrofit response that returns an observable:
#Headers({"Content-Type: application/json", "Accept: application/json"})
#GET("/api/v1/banners")
Observable<Response<GetBannersResponse>> getBanners(
#Header("Authorization") String auth_token);
The GetBannersResponse class is just a POJO to encapsulate my json response:
public class GetBannersResponse {
List<Banner> banners;
public List<Banner> getBanners() {
return banners;
}
public void setBanners(List<Banner> banners) {
this.banners = banners;
}
}
My Model (interactor) class I like to call DataHandler looks like this:
public class MyDataHandler implements MyDataHandlerContract.DataHandler {
private RetrofitAPI theCloud;
private PreferencesUtil prefs;
#Inject
public CatalogDataHandler(TaskrAPIConfig theCloud, PreferencesUtil prefs) {
this.theCloud = theCloud;
this.prefs = prefs;
}
#Override
public Observable<Response<GetBannersResponse>> getBanners() {
return theCloud.getApiService().getBanners(prefs.getTokenFormatted());
}
}
You can see that I'm returning the observable from the Retrofit call. Then in my presenter I just subscribe to this observable and act accordingly:
#Override
public void getBanners() {
dataHandler
.getBanners()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response<GetBannersResponse>>() {
#Override
public void onCompleted() {
//act on complete
}
#Override
public void onError(Throwable e) {
//act on error
}
#Override
public void onNext(Response<GetBannersResponse> getBannersResponseResponse) {
//act on result received
}
});
}
Related
I'm trying to learn MVVM to make my app's architecture more clean. But I'm having a hard time grasping how to create a "Domain" layer for my app.
Currently this is how the structure of my project is looking:
My View is the activity. My ViewModel has a public method that the activity can call. Once the method in the ViewModel is called, it calls a method in my Repository class which performs a network call, which then returns the data back to the ViewModel. I then update the LiveData in the ViewModel so the Activity's UI is updated.
This is where I'm confused on how to add a Domain layer to the structure. I've read a lot of Stackoverflow answers and blogs about the Domain layer and they mostly all tell you to remove all the business logic from the ViewModel and make a pure Java/Kotlin class.
So instead of
View --> ViewModel --> Repository
I would be communicating from the ViewModel to the Domain class and the Domain class would communicate with the Repository?
View --> ViewModel --> Domain --> Repository
I'm using RxJava to make the call from my ViewModel to the Repository class.
#HiltViewModel
public class PostViewModel extends ViewModel {
private static final String TAG = "PostViewModel";
private final List<Post> listPosts = new ArrayList<>();
private final MutableLiveData<List<Post>> getPostsLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> centerProgressLiveData = new MutableLiveData<>();
private final MainRepository repository;
#Inject
public PostViewModel(MainRepository repository) {
this.repository = repository;
getSubredditPosts();
}
public void getSubredditPosts() {
repository.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
#Override
public void onNext(#NonNull Response response) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(response.getData().getChildren());
getPostsLiveData.setValue(listPosts);
}
#Override
public void onError(#NonNull Throwable e) {
Log.e(TAG, "onError: getPosts", e);
centerProgressLiveData.setValue(false);
}
#Override
public void onComplete() {
}
});
}
public class MainRepository {
private final MainService service;
#Inject
public MainRepository(MainService service) {
this.service = service;
}
public Observable<Response> getSubredditPosts() {
return service.getSubredditPosts();
}
}
Could someone please give me an example of how I could do it? I'm quite lost here
I had a hard time while trying to figure out the domain layer.
The most common example of it is the use case.
Your viewmodel won't communicate directly to the repository. As you said, you need viewmodel 》domain 》repository.
You may think of a usecase as a abstraction for every repository method.
Let's say you have a Movies Repository where you call a method for a movie list, another method for movie details and a third method for related movies.
You'll have a usecase for every single method.
What's the purpose of it?
Let's say you have a DetailActivity that communicate with a Detail Viewmodel. Your viewmodel doesn't need to know all the repository (what's the purpose of calling a movie list method on you Detail screen?). So, all your DetailViewModel will know is "Detail Usecase " (that calls the Detail method in repository).
Google has updated the architecture documentation few hours ago, take a look!
https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html?m=1&s=09
PS: Usecase is not a special android class, you do not need to inherent any behavior (as fragment, activity, viewmodel...) it's a normal class that will receive the repository as parameter.
You'll have something like:
Viewmodel:
function createPost(post Post){
createUseCase.create(post)
}
UseCase
function createPost(post Post): Response {
return repository.create(post)
}
I spent quite a bit of time trying to learn how to add a domain layer using RxJava by reading a lot of blogs and Stackoverflow answers, but all of them were missing the conversion of the response from the api call to what you'd like to display on screen (For example if the back end returns a username dave123 and you'd like to display by dave123).
I finally figured it out and the secret sauce was to use a RxJava .map() operator inside the UseCase class. I also decided to keep the RxJava call inside my ViewModel.
So in my Repository class I have a method that calls the Api and returns a type of Single<Response>. This is the raw json data the Api returns.
public class MainRepository {
private final MainService service;
private final PostDao postDao;
#Inject
public MainRepository(MainService service, PostDao postDao) {
this.service = service;
this.postDao = postDao;
}
public Single<Response> getResponse() {
return service.getSubredditPosts();
}
}
Inside my GetPostsUseCase class, I'm call the getResponse() method from the MainRepository and altering the Response by performing business logic on it (the stuff I want to display on the UI. In this case I add the String "by " to the username)
And the secret or the part I had alot of trouble understanding/figuring out how to do was converting the Type inside the Single<>. I used the .map() operator to change the return type and filter the Response to a List<Post>
public class GetPostsUseCase {
private final MainRepository mainRepository;
#Inject
public GetPostsUseCase(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
public Single<List<Post>> getSubredditPosts(){
return mainRepository.getResponse().map(response ->
getPostsFromResponse(response.getData().getChildren())
);
}
private List<Post> getPostsFromResponse(List<Child> listChildren) {
List<Post> listPosts = new ArrayList<>();
for (Child child : listChildren) {
Post post = child.getPost();
post.setCreatedBy("by " + post.getUsername());
listPosts.add(post);
}
return listPosts;
}
}
And this is how my ViewModel looks like
public class PostViewModel extends ViewModel {
private static final String TAG = "PostViewModel";
private final List<Post> listPosts = new ArrayList<>();
private final MutableLiveData<List<Post>> getPostsLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> centerProgressLiveData = new MutableLiveData<>();
private final GetPostsUseCase getPostsUseCase;
#Inject
public PostViewModel(GetPostsUseCase getPostsUseCase) {
this.getPostsUseCase = getPostsUseCase;
getSubredditPosts();
}
public void getSubredditPosts() {
getPostsUseCase.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Post>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
#Override
public void onSuccess(#NonNull List<Post> list) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(list);
getPostsLiveData.setValue(listPosts);
}
#Override
public void onError(#NonNull Throwable e) {
centerProgressLiveData.setValue(false);
}
});
}
I couldn't find any blogposts or answers that had an example like this. Hopefully this helps anyone out there who is struggling to learn how to implement clean architecture with MVVM, Hilt, RXJava and a Domain layer.
If I did do something incorrectly or not considered clean architecture please let me know.
i'm working on project where i have to insert and delete data from room db , so basically i was using the old approach which is to implement Asynctask for background operations but since it is no longer recommended , i decided to use Rxjava instead , i tried to implement it but i'm not getting any result so far , and this is a piece of code where it shows the insertion of data
Completable.fromAction(new Action() {
#SuppressLint("CheckResult")
#Override
public void run() throws Exception {
recordingDb.insertRecording(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
And this is the deletion method
public void DeleteData(modelUidd modelUidd) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
recordingDb.delete(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
So basically i tried to use completable with the operator fromaction , i'm not sure if what i implemented is correct or not , any help would appreciated guys , thank you
The problem is that you are actually not subscribing to the observables, so nothing is happening.
To subscribe to an observable, you have to call the .subscribe() method.
I suggest that your methods defined in your DAO classes (or you "repository" classes), such as DeleteData in your example, return the Observable. Then, you can call the method in the DAO to get the Observable and subscribe to it from (ideally) a ViewModel or, if not, directly from an Activity. The moment you call the subscribe you will trigger the actual insertion or deletion, and will get a response from the onSuccess or onError defined callbacks.
For example:
public class MyViewModel extends ViewModel {
private MyRepository myRepository;
private final CompositeDisposable disposables;
#Inject
public MyViewModel(MyRepository myRepository) {
...
this.myRepository = myRepository;
disposables = new CompositeDisposable();
...
}
public void callObservableInRepository() {
disposables.add(myRepository.myObservable()
.subscribe(onSuccess -> {...} , onError -> {...}));
}
#Override
protected void onCleared() {
disposables.clear();
}
}
You can also check these two other answers for more information:
About async operations in RxJava
Using CompositeDisposable in ViewModel
Is this a good approach or I've just found a nasty workaround?
I'm using MediatorLiveData class because seems useful to update the source of a LiveData object.
I mean, the majority of tutorials that I've found on internet just use Livedata or MutableLivedata without a dynamic source, in example:
fun search(/*no input params*/): Call<List<Person>>
But in my case, I have the following web service that performs a search by name:
interface APIServidor {
#GET("search")
fun search(#Query("name") name: String): Call<List<Person>>
}
public class PeopleRepository {
public LiveData<List<Person>> search(String name){
final MutableLiveData<List<Person>> apiResponse = new MutableLiveData<>();
Call<List<Person>> call = RetrofitService.Companion.getInstance().getApiServer().search(name);
call.enqueue(new Callback<List<Person>>() {
#Override
public void onResponse(#NonNull Call<List<Person>> call, #NonNull Response<List<Person>> response) {
if (response.isSuccessful()) {
apiResponse.postValue(response.body());
}
}
#Override
public void onFailure(#NonNull Call<List<Person>> call, #NonNull Throwable t) {
apiResponse.postValue(null);
}
});
return apiResponse;
}
}
Then in the viewmodel class I'm adding source per new request.
public class SearchViewModel extends ViewModel {
private MediatorLiveData<List<Person>> mApiResponse;
private PeopleRepository mApiRepo;
public SearchViewModel() {
mApiResponse = new MediatorLiveData<>();
mApiRepo = new PeopleRepository();
}
public LiveData<List<Person>> getPlayers() {
return mApiResponse;
}
public void performSearch(String name){
mApiResponse.addSource(mApiRepo.search(name), new Observer<List<Person>>() {
#Override
public void onChanged(List<Person> apiResponse) {
mApiResponse.setValue(apiResponse);
}
});
}
}
Activity
bt_search.setOnClickListener {
val player_name = et_player.text.toString()
viewModel.performSearch(player_name)
}
Project scope
I'm in a personal project
Goals
Use MVVM + Live data + Repository pattern
Problem
I've only found tutorials with a simple approach: observe a LiveData object that access to a repository object and fetch data only once.
In example: Fetch all people (select * from people) from web service.
My case: Fetch people that mach a name (select * from people where name=?) from web service.
https://medium.com/#elye.project/kotlin-and-retrofit-2-tutorial-with-working-codes-333a4422a890
https://medium.com/#sriramr083/error-handling-in-retrofit2-in-mvvm-repository-pattern-a9c13c8f3995
Doubts
Is a good idea use MediatorLiveData class to merge all requests took from user input?
Should I use MutableLiveData and change the repository class and use a custom clousure?
Is there a better approach?
I was using this pattern with MediatorLiveData as well, but it forms an issue.
From the user perspective it seems to function just fine, but one problem here is that every time you call performSearch() the repository creates a new LiveData object which is additionally added to MediatorLiveData via addSource().
An idea might be to have the repository create the MutableLiveData object only once and on consecutive call just update it's value. So f.e. MutableLiveData<List<Person>> apiResponse; would be a non initialized private field that gets initialized in the search() method.
Eg. if (apiResponse == null) apiResponse = new MutableLiveData();
I have defined and interface, with an endpoint that returns JSON. Retrofit converts this JSON into MyObject. It could be also a list, map, etc, it doesn't matter now.
This is how I subscribe.
subscription = Retrofit.create(MyApi.class)
.doSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyObject>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyObject myObject) {
}
});
My question is:
Is it possible that onNext is called more than once?
If yes, in which occasion?
In your case, no it's impossible, of course if you do not emit more items in doSomething() method.
But there is another, quite usual cases, for instance, if you use Local first approach and subscribing on hot observable which will emit new item each time when data in data base has change.
E.g. using retrofit:
#Override
public Observable<List<FollowMeUser>> getFollowMeUsers() {
return realm.where(FollowMeUser.class)
.findAll()
.asObservable()
.filter(RealmResults::isLoaded);
}
getFollowMeUsers()
.subscribe(users -> {Timber.d("saved data has changed")}, Timber::e);
Each time when you will insert/modify/delete FollowMeUser collection, all subscribers of getFollowMeUsers will be notified.
If your retrofit returns an array/list of data, onNext is called multiple times.
But if your retrofit returns a single data objext, onNext will be called only once.
Example:
//POJO
class User {
int userId;
String UserName;
}
//POJO
class UserData {
List<User> users;
}
interface RetrofitGithub {
#GET("...")
Observable<List<User>> getUsers();
#GET("...")
Observable<UserData> getUserData();
}
If you subscribe to getUsers() onNext will be called multiple N times.(N = size of the list)
If you subscribe to getUserData() onNext will be called only once.
I'm using an API which always returns JSON object that looks like this:
public class ApiResponse<T> {
public boolean success;
public T data;
}
data field is a JSON object that holds all valuable information. Of course it's different for different requests. So my retrofit interface looks like this:
#GET(...)
Observable<ApiResponse<User>> getUser();
And when I want to handle response I need to do eg.:
response.getData().getUserId();
I don't really need that boolean success field and I'd like to omit it, so that my retrofit interface could look like this:
#GET(...)
Observable<User> getUser();
Is it possible to do that in Gson? Or maybe a neat Rx function which will automatically transform this?
EDIT:
Example json:
{
"success": true,
"data": {
"id": 22,
"firstname": "Jon",
"lastname": "Snow"
}
}
EDIT 2:
I've manged to do this with retrofit interceptor where I manually modify response body. It works but if you got any other suggestions, please post them :)
As Than said, solution with the interceptor is not so great. I've managed to solve this with a Rx transformer. I've also added custom api exception that I can throw when something went wrong and easy handle it in onError. I think it's more robust.
Response wrapper:
public class ApiResponse<T> {
private boolean success;
private T data;
private ApiError error;
}
Error object returned when success is false:
public class ApiError {
private int code;
}
Throw this exception when success is false:
public class ApiException extends RuntimeException {
private final ApiError apiError;
private final transient ApiResponse<?> response;
public ApiException(ApiResponse<?> response) {
this.apiError = response.getError();
this.response = response;
}
public ApiError getApiError() {
return apiError;
}
public ApiResponse<?> getResponse() {
return response;
}
}
And a transformer:
protected <T> Observable.Transformer<ApiResponse<T>, T> applySchedulersAndExtractData() {
return observable -> observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(tApiResponse -> {
if (!tApiResponse.isSuccess())
throw new ApiException(tApiResponse);
else
return tApiResponse.getData();
});
}
As far is I know, there is not an easy solution to achieve what you want. By easy solution I mean something what is more reasonable than what you have now. You can deserialize all the data by yourself, you can strip data using interceptors but it's a lot more effort that just using the API the way it's now.
Also, think about what if it changes and some field apperas right next to that item and boolean (for example long last update time), you will have to change everything.
The only reasonable idea I have is to wrap your Api interface with another class and inside it call .map on your Observable to transform ApiResponse<T> into T.