RxJava - Observable which emits items which are new or changed - android

I am a newbie in reactive world and trying to implement the following scenario with rxjava/rxandroid 2.x.
I have a local data set as ArrayList mItems in Application class. The same data set is synchronized with server and updated every time user opens the app. However before server returns the response, I want to display the local data set in RecycleView backed by adapter. As soon as the response is returned, the adapter should update the list with delta and without disturbing the order in the UI.
So far I have tried this:
public Observable<List<Item>> getItemsObservable() {
Observable<List<Item>> observeApi = itemServiceAPI.getItemsForUser(accountId);
if (mItems != null) {
return Observable.just(mItems).mergeWith(observeApi);
} else {
return observeApi;
}
}
To update the UI, the above method is invoked like this:
Observable<List<Item>> itemsObservable = appContext.getItemsObservable();
itemsObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultObserver<List<Item>>() {
#Override
public void onNext(List<Item> Items) {
// Code to update the adapter
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
With this I get the onNext called twice for each local data set and remote data set. How to achieve the desired functionality? Does it need use of filter operators to exclude items?
What's the best approach to achieve this?

You can use 'startWith' operator: it subscribes to different observable first.
appContext.getItemsObservable()
.startWith(localCacheObservable)
.subscribe(adapter::updateData)
Adapter's update data should handle diff calculations.

UPDATE
First of all, why are you using Observable.just(mItems) ??? That's unnecessary.
Your code should look like
itemServiceAPI.getItemsForUser(accountId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultObserver<List<Item>>() {
#Override
public void onNext(List<Item> Items) {
// Code to update the adapter
mAdapter.updateItems(items);
/* method in adapter class
*
* public void updateItems(List<Item> mList) {
this.items.addAll(mList);
notifyDataSetChanged();
}
* */
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Here, your adapter will be updated in onNext. make sure before calling API, you have to set your adapter with local items.

Related

RxJava - fetch every item on the list and emits one-by-one (any order)

I have a method that returns an Observable<List<Long>>, which are ids of some Items. I'd like to go through this list and download every Item using another method that returns Observable<Item>. currently I'm doing it by below code.
#Override
public Observable<List<Item>> getResponses(List<Long> requests) {
return Observable.from(requests).
flatMap((Func1<Long, Observable<Item>>) restRequest -> getResponseEach(restRequest)).toList();
}
It's working fine, but it returning all the response in on go, I mean when all download get finish then my onNext() get invoked,
Main Question But alternatively I need to emit every response one-by-one(Any Order) once each item fetched successfully from server, so my onNext should be invoked every-time time individually for each item.
How would I do this using RxJava operators?
You have to remove the toList() operator. The toList() will emit only after all the emissions of the upstream have been completed, and it will collect the results and will emit as a Single<List<YourResultObject>>
You can return the observable returned by the flatMap in your code and you will get the results one by one,
public Observable<Item> getResponses(List<Long> requests) {
return Observable.fromIterable(requests)
.flatMap(restRequest -> getResponseEach(restRequest));
}
Now, your getResponses method will return Observable<Item> instead of Observable<List<Item>>
And you can subscribe to this function as follows
getResponses(ids)
.subscribe(new DisposableObserver<Item>() {
#Override
public void onNext(Item item) {
// each item will be received here one by one
}
#Override
public void onError(Throwable e) {
// handle any occured error during the operation
}
#Override
public void onComplete() {
// all operations completed
}
});

RxJava Take operator is not working

What I want to achieve: I am loading data from some web service in recyclerView. I want to load first 10 data and display it in recyclerView. When User scrolls, call web service for another 10 data to display it.
What I have done: For above aim, I'm using .take operator of RxJava. But It seems not working for me or else I'm doing some mistake.
What issue I'm having: I'm getting all the data instead of first 5 data. There might be something that I'm missing it.
My code is like below.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
private RecyclerView recyclerView;
private ProgressDialog mProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
setupProgressDialog();
ApiEndPoints apiEndPoints = ApiClient.getClient().create(ApiEndPoints.class);
mProgressDialog.show();
apiEndPoints.fetchAllUsers()
.take(5)
.subscribeOn(Schedulers.io()) //Background Thread
.observeOn(AndroidSchedulers.mainThread()) //UI Thread
.subscribe(new Observer<List<Pojo>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Pojo> pojoList) {
recyclerView.setAdapter(new RVAdapter(pojoList));
Log.e(TAG, "List Size: " + pojoList.size());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
mProgressDialog.dismiss();
}
}
);
}
private void setupProgressDialog() {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("Loading . . . ");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setCancelable(false);
}
}
Ask me if anything required. Thanks in advance.
If you use take operator, It will emit first 5 items and than onCompleted function will trigger. So you need re observable source.
I found example application on github which is using rxjava for load more operation.
You receive a list, which is considered as an object. You need to iterate them, take first 5 and then get them back as a list:
apiEndPoints.fetchAllUsers()
// iterate users
.concatMap(new Func1<List<Pojo>, Observable<Pojo>>() {
#Override
public Observable<Pojo> call(List<Pojo> pojos) {
return Observable.from(pojos);
}
})
// take 5 from iteration
.take(5)
// put all 5 users back to a list
.toList()
.subscribeOn(Schedulers.io()) //Background Thread
.observeOn(AndroidSchedulers.mainThread()) //UI Thread
If you had an array (Pojos[] users) take() operator should work without iteration.

How to wait for list of Maybe items to complete in RxJava2?

Apologies in advance if I lack a basic understanding of how to use RxJava2, because this seems to me something that should be quite fundamental. I've wracked my brains with unsuccessful Google searches, so welcome any resource recommendations. I've opted to use a 'sanitized' representation of my workaround code for the sake of clarity.
Problem description
I have an RxJava2 function asyncCallForList() that returns a Maybe<Arraylist<CustomClass>>. Each CustomClass object in this list only has a few basic fields populated (e.g. the source database only contains a unique identifier and a title string for each item).
The full data required for each item is in another database location, which is retrieved using another function asyncCallForItem(uid), which returns a Maybe<CustomClass> based on the unique identifier, where the encapsulated CustomClass has all the required data. This function is to be called for each item in the list returned by asyncCallForList().
The desired functionality is to update my UI once all the objects in the list have been populated.
Workaround #1
It is easy enough to loop through the resulting array list in the doOnSuccess() attached to the initial Maybe<Arraylist<CustomClass>>, then update my UI in the doOnSuccess() on the Maybe<CustomClass> returned by the subsequent asynchronous calls. This is not an acceptable workaround as there will be an unknown number of UI updates being made (the initial list returned could have any amount of items) and will hurt performance.
Workaround #2
This gets the desired outcome but feels like the wrong way to go about it - I suspect there is a more elegant RxJava2 solution. Basically, I create a custom Observable in which loop through the items in the list and get the full data for each. However, rather than update the UI each time I populate a CustomClass item, I increase a counter, then check if the counter exceeds or equals the initial list size. When this condition is met I call the onComplete() method for the observable's emitter and update the UI there.
private void fetchRemoteDataAndUpdateUi() {
//Counter reset to zero before any asynchronous calls are made.
int count = 0;
Maybe<ArrayList<CustomClass>> itemList = asyncCallForList();
Consumer<ArrayList<CustomClass>> onListReturnedSuccess;
onListReturnedSuccess = new Consumer<ArrayList<CustomClass >>() {
#Override
public void accept(ArrayList<CustomClass> list) throws Exception {
//Custom observable created here, in which the resulting array list is processed.
listObservable = Observable.create(new ObservableOnSubscribe<CustomClass>() {
#Override
public void subscribe(final ObservableEmitter<CustomClass> e) throws Exception {
for (CustomClass customClass : list) {
final CustomClass thisCustomClass = customClass;
//Call to get full data on list item called here.
asyncCallForItem(customClass.getUid())
.doOnSuccess(new Consumer<CustomClass>() {
#Override
public void accept(CustomClass customClass) throws Exception {
thisCustomClass.update(customClass);
e.onNext(thisCustomClass);
count++;
if (count >= list.size()) {
e.onComplete();
}
}
}).subscribe();
}
}
});
listObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<CustomClass>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(CustomClass customClass) {
//Here I add the populated CustomClass object to an ArrayList field that is utilised by the UI.
listForUi.add(customClass);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
//Here the UI is finally updated once all CustomClass objects have been populated.
updateUi();
}
});
}
};
//Kick everything off.
itemList.doOnSuccess(onListReturnedSuccess).subscribe();
}
flatMap it!
asyncCallForList()
.subscribeOn(Schedulers.io())
.flatMapSingle(list ->
Flowable.fromIterable(list)
.flatMapMaybe(item ->
asyncCallForItem(item.id)
.subscribeOn(Schedulers.io())
.doOnSuccess(response -> {
// copy state from the original item
response.text = item.text;
})
, 1) // number of concurrent item calls
.toList()
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(successList -> { /* update UI */ }, error -> { /* report error */ });

Rx SearchView needs to cancel on going request with less priority

I am using
RxSearchView.queryTextChangeEvents
to detect the events of “Search As You Type” and also when you submit a search,

SAYT is to get suggestions and when you do a submit, it executes a full search.
There are 2 problems I am having at the moment.
When you are typing, and getting suggestions, but suddenly you click submit then it executes the full search but the problem is that if there is an on going suggestion request there might be the case that they appear on screen when they should not as the full search has priority.
So I would like to cancel (unsubscribe) the request from the suggestions in case there is a submit on the full search.
How can I achieve this with this code?
Another problem is that when I am deleting the search term in the search view and it gets empty, then it clears the adapter but there are cases when I clear the search text, there is still an on going suggestions request and it sets the results but it just clear, so I would like to guarantee that if the user clears the searchview, it cancels all the requests.
Here is the Code I am using.
RxSearchView.queryTextChangeEvents(searchView)
.skip(1)
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
final boolean empty = TextUtils.isEmpty(searchViewQueryTextEvent.queryText());
if (empty) {
//Dont show anything clear adapter
clearAdapter();
}
return !empty;
}
})
.concatMap(new Func1<SearchViewQueryTextEvent, Observable<Object>>() {
#Override
public Observable<Object> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String searchTerm = searchViewQueryTextEvent.queryText().toString();
boolean submitted = searchViewQueryTextEvent.isSubmitted();
//Hide RecyclerView
//Show loading indicator
showLoading();
Observable<Object> observable;
if (submitted) {
observable = getFullSearch(searchTerm);
} else {
observable = getSuggestionsSearch(searchTerm);
}
return observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnCompleted(new Action0() {
#Override
public void call() {
//Show RecyclerView
//Hide loading indicator
showContent();
}
});
}
})
.subscribe(new Subscriber<Object>() {
#Override
public void onNext(Object object) {
//Set data on recyclerview and notify change.
setData(object);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});
}
You might want to try switchMap instead, it just uses the last emited value from the observable.
From the docs:
Returns a new Observable by applying a function that you supply to each item emitted by the source Observable that returns an Observable, and then emitting the items emitted by the most recently emitted of these Observables.
The resulting Observable completes if both the upstream Observable and the last inner Observable, if any, complete. If the upstream Observable signals an onError, the inner Observable is unsubscribed and the error delivered in-sequence.

Can't update arraylist after API call in Android Studio

I am trying to sort an arraylist in alphabetical order after it has been retrieved from the API call:
StarWarsApi.init();
StarWars api = StarWarsApi.getApi();
m_vwPeopleLayout.setAdapter(m_peopleAdapter);
api.getAllPeople(i, new Callback<SWModelList<People>>() {
#Override
public void success(SWModelList<People> planetSWModelList, Response response) {
for (People p : planetSWModelList.results) {
peopleArrayList.add(p);
}
}
#Override
public void failure(RetrofitError error) {
System.out.print("failure");
}
});
//code to sort arrayList
m_peopleAdapter.notifyDataSetChanged();
The code to sort the list and update the Adapter gets ran, but before the API call is finished. I'm guessing this is caused by the thread not finishing in time. I've tried putting a sleep statement before sorting but it seems that pauses the entire activity.
How can I wait until the API call is finished before running more code?
m_peopleAdapter.notifyDataSetChanged();
should be in your Api-Callback like this
api.getAllPeople(i, new Callback<SWModelList<People>>() {
#Override
public void success(SWModelList<People> planetSWModelList, Response response) {
for (People p : planetSWModelList.results) {
peopleArrayList.add(p);
}
m_peopleAdapter.notifyDataSetChanged();
}
#Override
public void failure(RetrofitError error) {
System.out.print("failure");
}
});
You need to notify the adapter after the dataset was changed. Your code triggered the notification just after the call was started.
How can I wait until the API call is finished before running more
code?
Use success method is called on UI Thread when request is finished. do all Adapter setting or call notifyDataSetChanged inside success method to update Adapter data after getting it from API:
#Override
public void success(....) {
for (People p : planetSWModelList.results) {
peopleArrayList.add(p);
}
// call notifyDataSetChanged here
m_peopleAdapter.notifyDataSetChanged();
}

Categories

Resources