Trouble connecting RxPagingSource to a Volley call using Java - android

I am trying to Load and display paged data from Volley and having a little trouble connecting the data source to my backend service. Most examples are in Kotlin but I have been following the Android documentation and a LoopWiki example. Since the latter is using Retrofit and I am using Volley I have been trying to bounce between the two to come up with something.
This piece of code from the Android documentation shows the call but uses a hypothetical backend. I would like to plug my Volley call into this:
#NotNull
#Override
public Single<LoadResult<Integer, User>> loadSingle(
#NotNull LoadParams<Integer> params) {
// Start refresh at page 1 if undefined.
Integer nextPageNumber = params.getKey();
if (nextPageNumber == null) {
nextPageNumber = 1;
}
return mBackend.searchUsers(mQuery, nextPageNumber)
.subscribeOn(Schedulers.io())
.map(this::toLoadResult)
.onErrorReturn(LoadResult.Error::new);
}
When I have been using volley I have been passing in a listener and calling it
public void getData(Application application, ResultsListener listener) {
...
if (listener!=null)
listener.onReturn(arrayList);
}
public interface ResultsListener {
void onReturn(List<MyObject> objectList);
}
But this approach doesn't seem like a good fit here. I am somewhat new and have gotten in the habit of using listeners but I expect there is a better approach.
What do I need to do to get .subscribeOn, .map, and .OnErrorReturn on a search method in a class?
I have the paging functioning in my API.

Related

Changes are not observable by viewmodel

I have created an app which is relied on my local server which fetch profile image and information about user..Code works fine without any problem but when I change my data in the local server (for example profile picture )the updated profile is not reflecting in the application until activity is restarted but this should not be happened because live data should reflect the change immediately as soon as changes occurred in the database.
below is the code of live data class
private MutableLiveData<Profile> profileMutableLiveData;
public void init(String token){
if (profileMutableLiveData!=null){
return;
}
repository=Repository.getInstance();
profileMutableLiveData=repository.getProfile(token);
}
public LiveData<Profile> getProfile(){
return profileMutableLiveData;
}
here is my Repository code
public class Repository {
private static Repository instance;
public static Repository getInstance(){
if (instance==null){
instance=new Repository();
}
return instance;
}
public MutableLiveData<Profile> getProfile(String token){
MutableLiveData<Profile> data=new MutableLiveData<>();
RetrofitApi retrofitApi=RetrofitInstance.getInstance();
Call<Profile> call=retrofitApi.getProfile(token);
call.enqueue(new Callback<Profile>() {
#Override
public void onResponse(Call<Profile> call, Response<Profile> response) {
Profile profile=response.body();
if (response.isSuccessful()){
data.setValue(profile);
}
}
#Override
public void onFailure(Call<Profile> call, Throwable t) {
}
});
return data;
}
}
Code in main activity to observe changes....
actually I am showing profile image in navigation drawer ... like telegram app
viewModelClass = new ViewModelProvider(this).get(ViewModelClass.class);
viewModelClass.init(token);
viewModelClass.getProfile().observe(this, new Observer<Profile>() {
#Override
public void onChanged(Profile profile) {
Picasso.get().load("http://192.168.43.216:8000" + profile.getProfile_photo()).into(profileImage);
fName = profile.getFirst_name();
lName = profile.getLast_name();
image = profile.getProfile_photo();
nameView.setText("Hello " + profile.getFirst_name());
}
});
}
The code is working fine but I want the data must be updated as soon as changes made in my server...
but data is updated when I restart the activity or opening app again after closing the activity...
May be the problem - is that you begin to observe in your activity one instance of MutableLiveData, and then you replace it with another one.
In your ViewModel:
profileMutableLiveData=repository.getProfile(token);
you override it instead of setting new value with "postValue"
In your Repository:
MutableLiveData<Profile> data=new MutableLiveData<>();
you make another instance of LiveData
You can try to change your return value from a Repository to a "Profile" and set it as a new value of MutableLiveData in your ViewModel with "postValue"
UPDATED
I've read your question more carefully. I think my answer above wouldn't give you what you expect (in case you expect Retrofit should update LiveData instantly like ROOM does)
So my thoughts:
You expect too much using LiveData+Retrofit. Just using them doesn't mean you'll get on-line updates of your data on your server. To achieve that you have to change mechanism of your interaction with your server, not just fix few lines in code you've shown.
There is mechanism LiveData+ROOM that works with local DB (Sqlite) in a way, that you expect from LiveData+Retrofit. But there is no magic there. Room is using mechanic, that built-in in Sqlite for notifying (triggering) when there are some changes in DB tables occur. But Retrofit doesn't implement similar mechanism with Rest Api and actually it's not its responsibility.
To achieve what you want you can look at several possibilities:
To use some Cloud Service API, that contains that built-in mechanism for notifying your device when data changes (Firebase, for example)
To implement some kind of periodic synchronisation of your app data with server. After this synchronisation you'll have all data on device and depending on where you put your data you could observe changes with LiveData+Room or FileObserver.
To simplify your case and refresh your data from the server at activity explicitly after click on Button "Refresh" on your activity. In that case you can implement steps that I wrote at first version of my answer.

Android MVVM architecture ViewModel not calling one by one

I am working with MVVM pattern for api call and working good.
My issue is in my activity i created three different ViewModels for calling different APIs based on some API response.
Now when one API execute then some value from its response needs to pass to other API view model so using that i can get response. But some how in my activity all there view models called at same time so i m not getting proper response.
So what is the best way to register observe using MVVM one by one so it work proper.
void getInspirationDetails() {
inspirationDetailsModel.getInspirationDetails(userid, deviceId, inspirationId, Constants.PLATFORM).observe(InspirationUploadDetailsActivity.this, new Observer<MainInspirationDetailsResponse>() {
#Override
public void onChanged(MainInspirationDetailsResponse mainInspirationDetailsResponse) {
}
}
void getProducts() {
productsDetailsModel.getProductsItems(inspirationId, "", "N", "ALL", "", "").observe(this, new Observer<MainProductsResponse>() {
#Override
public void onChanged(MainProductsResponse mainProductsResponse) {
}
}
});
Both functions called at same time which i don't need.
Thanks in Advance!``

Android MVP - RxJava and retrofit - best approach

I'm figuring out how to develop an Android app, using MVP, RxJava2 and retrofit.
In my presenter, here is the code:
public void loadData() {
compositeDisposable.dataModelRepository.getDataList().subscribeOn(Schedulers.io())
.observeOn(mainScheduler).subscribe(new Consumer<List<Data>>() {
#Override
public void accept(List<Data> dataList) throws Exception {
if (!dataList.isEmpty())
view.displayData(dataList);
else
view.displayEmpty();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
System.out.println(throwable.toString());
view.displayError("boooom");
}
});
}
Retrofit interface has been defined in the following way:
#GET("/fooURL")
Single<List<Data>> getDataList();
And the repository is just
public Single<List<Data>> getDataList() {
return retrofitApi.getDataList();
}
And it is working fine. Question is as follows: my intention is to fetch network data only when data is not available locally, in db.
Having this in mind, is it correct that schedulers are managed in the presenter? Or should they be managed in the Repository?
My guess is that presenter is the correct place, as it creates a thread so repository can do its stuff sequentially (fetch db, if nothing, then fetch network/cache; return data wherever it has been fetched), and when data is provided, notify the view inside the accept method of the Consumer.
Is it correct? Or should it be done in a different way?
Another point is: how can i test using Mockito the repository? The dataModelRepository.getDataList() method i mean? Not sure how to do any Assert for Single objects...
Thanks in advance!
I suggest you to offload all business logic that is related to fetching, retrieving data to a central repository.
One way to achieve somewhat similar to what you have described is to use a concat operator.
Observable<List<Data>> getData() {
return Observable
.concat(localRepository.getData(), remoteRepository.getData())
.first();
}
This will try to get data from your local repository first and if it has no data it will make a network request.
I assume your local and remote repositories will be observed on a new thread, but if you need to perform any action on the UI, simply subscribe on a main thread in your presenter.

Passing parameter to Observable.create

I am using RXJava on Android for asynchronously access the database.
I want to save an object in my database.
In this way, I created a method which take a final parameter (the object I want to save) and returns an Observable.
At this point I don't care to emit anything so I will call subscriber.onComplete() at the end.
Here is my code:
public Observable saveEventLog(#NonNull final EventLog eventLog) {
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(Subscriber<? super Object> subscriber) {
DBEventLog log = new DBEventLog(eventLog);
log.save();
subscriber.onCompleted();
}
});
}
The thing is, I saw many answer using the final keyword for the parameter, but I would like to do this without it.
The reason is I don't really like the approach of declare a final variable in order to use it in another thread.
Is there any alternative? Thanks.
We usually suggest avoiding the use of create because it may seem simple to use it but they usually violate the advanced requirements of RxJava. Instead, you should use one of the factory methods of Observable. In your case, the just factory method will get what you wanted: no final parameter:
public Observable<?> saveEventLog(#NonNull EventLog eventLog) {
return Observable
.just(eventLog)
.doOnNext(e -> {
DBEventLog log = new DBEventLog(e);
log.save();
})
.ignoreElements();
}

Combining API calls with RX Java

I'm new to RXJava and i'm having trouble understanding how to chain together the result of API calls.
I'm making two API calls using retrofit, A and B, which both return an observable List of objects. Both API calls are independent so I want to make both at the same time, but to achieve my final result, I need to first take the result of A, do some work, then combine that with the result of B to populate my list adapter.
Make API Call A
Make API Call B
Take A's result and create result X
Take Result of B + X and populate adapter
#GET("/{object_id}/object_a")
Observable<List<Object_A>> getObjectAList(
#Path("object_id") long object_id);
#GET("/{object_id}/object_b")
Observable<List<Object_B>> getObjectBList(
#Path("object_id") long object_id);
This is where I get lost trying to use RX java. I can take the result of api call A and do my work
but I'm not sure how to take the result I just generated and combine it with API Call B.
aService. getObjectAList(object_a.getID())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(new Action1<List<Object_A>>() {
#Override
public void call(List<Section> sections) {
// Do Stuff Here...
// Now i need to take this result and combine it with API Call B...
}
});
I want to make both API calls at the same time, but i'm not sure how to chain together and combine API calls. Any help is appreciative.
Something like this?
Observable
// make api call for A list and B list
.combineLatest(getObjectAList(), getObjectBList(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(List<Object_A> o, List<Object_B> o2) {
// Do what you need to do in the background thread
List x = createX(o);
List y = createY(x, o2);
return y;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object y) {
// UI thread, do what you need e.g. renders the list
mAdapter.setList(y);
}
});
Taking care of replacing the proper types should bring you quite close to the solution.
The question is : how would you combine results ?
Building a new result from List and List ? Combine A objects with B objects ?
Answer to this question help to find the right operator for your problem.
A simple example of combining results can be this :
getObjectAList().zipWith(getObjectBList(), (aList, bList) -> // combine both list to build a new result)).subscribe()
You can combine elements of the list too with another operator (combineLatest for example)
aObs = getObjectAList().flatMap(Observable::from);
bObs = getObjectBList().flatMap(Observable::from);
Observable.combineLatest(aObs, bObs, (a,b) -> // combine a object with b object).subscribe();
For all of this examples above, requests will be done in parallel by retrofit.
I'd probably do something like the following
Observable convertedObservable = getObjectAList
.map(object_as -> convertAToX(object_as));
Observable.combineLatest(convertedObservable, getObjectBList, (listx, listb) -> {
return listx.addAll(listb);
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(r -> {
setAdapterWith(r);
});
Keep in mind this is using lambdas instead of anonymous classes but you should get the gist. Map is a great way of converting one object type to another (results of A to Results of X). So you can decide how convertAToX method works for you. Then you can use combineLastest on the converted A-X and B to return the list of R which updates your adapter
Ideally this is all in a ViewModel of some kind where getObjectAList and getObjectBList can me mocked on with Mock observables and you can test all the logic easily :)

Categories

Resources