RXJava create an observable on completion of other - android

Hi I have been quite struggling with this for a while. Any help is appreciated.
I have a requirement to run one observable after completion of another observable. So e.g. The following code creates an observable from input value to value + 10.
Observable<ColoredIntegerModel> getSequenceObservable(int value, int delay, int color) {
return Observable.range(value,10)
.map(i -> {
Log.d(TAG, "Value " + i
+ " evaluating on " + Thread.currentThread().getName()
+ " emitting item at " + System.currentTimeMillis());
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
}
return new ColoredIntegerModel(i, color);
});
}
The ColorIntegerModel is as follows
public class ColoredIntegerModel {
private Integer mValue;
private int mColor;
public ColoredIntegerModel(Integer value, int color) {
mValue = value;
mColor = color;
}
public Integer getValue() {
return mValue;
}
public int getColor() {
return mColor;
}
}
I create the two observables as follows and concat them like so .
Observable<ColoredIntegerModel> observable1 = getSequenceObservable(1, 1000, Color.BLUE);
Observable<ColoredIntegerModel> observable11 = getSequenceObservable(11, 1000, Color.RED);
mDisposable =
observable1.concatWith(observable11)
.doOnDispose(() -> {Log.d(TAG, "observable disposed");})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.repeat(2)
.subscribe((m) -> {
Utils.appendColoredText(mResultTextView, "Adding item "
+ m.getValue().toString() + "\n", m.getColor());
});
The above code prints 1..10 (in blue each item delayed by 1s) and 11..20 (in red).
So far so good.
But my requirement is to create the second observable only after the first is complete. Infact it could be array of observables, where the n+1 observable is only created after the nth is done. Each observable can emit multiple items. Is there any operator to achieve this?

I don't know if I understood it right but if you want to create the Observable when you subscribe to it you need the defer operator

You can start next observable from doOnCompleted() of previous one
Observable<MyData> observable1 = ...;
Observable<MyData> observable2 = ...;
Observable<MyData> observable3 = ...;
Observable
.concat(observable1.doOnCompleted(this::onCompleteObservable1),
observable2.doOnCompleted(this::onCompleteObservable2),
observable3.doOnCompleted(this::onCompleteObservable3))
...
...
Hope this help.

Related

Looping around while using RxJava with Room

I had been trying to use RxJava with Room. The logic is simple, if there is no row for current date, I will create an instance and insert the row into the databse. The problem is that the row is being inserted but its acting like a loop. When I debug, the code goes to run insertTestType, it completes and then comes to the subscriber part of getTestModel and then the counter is 1 and then stops.
private void getTestModel() {
String date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date());
mCompositeDisposable.add(questionDatabase.questionDao().getTestByDate(date, testType)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(testModels -> {
if (testModels.size() > 0) {
testModel = testModels.get(0);
} else {
testModel = new TestModel(testType, date);
insertTestType();
}
}, throwable -> Log.e("ErrorRx", "exception getModels")));
}
private void insertTestType() {
Completable.fromAction(() -> questionDatabase.questionDao().insert(testModel))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableCompletableObserver() {
#Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError");
}
});
}
The database is,
#Insert
void insert(TestModel testModel);
#Query("SELECT * FROM " + TestModel.TABLE_NAME+ " WHERE " + TestModel.DATE + " = :date" + " AND testType " + "=:testType")
Flowable<List<TestModel>> getTestByDate(String date, TestType testType);
why its coming back to previous subscriber? Thanks in advance.
The code is running just as it should be :)
When you use Flowable, every time the data is updated the Flowable object will emit automatically, notifying all it's subscribers and that's why it invokes code from subscribe in your first method.
If you don't want such behaviour consider using Maybe - more informations here

Stop rxJava observable chain execution on disposing

While debugging rxJava network call in an app i came across to a situation, that if we dispose or clear disposal object returned by subscription of a chain of observables then only first observable gets disposed not other chained observables by flatMap.
Have a look at following demo code snippet:
CompositeDisposable testCompositeDisposal = new CompositeDisposable();
private void testLoadData() {
Disposable disposable = Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "First: " + i);
sbr.onNext(true);
}
sbr.onComplete();
}).subscribeOn(Schedulers.io()).flatMap(value -> Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "Second: " + i);
sbr.onNext(true);
}
sbr.onComplete();
})).doOnNext(value -> {
Log.w("Debug: ", "doONNext");
}).doOnDispose(()-> {
Log.w("Debug: ", "doOnDispose: observable has been disposed");
}).subscribe();
testCompositeDisposal.add(disposable);
}
#Override
public void onStop() {
super.onStop();
testCompositeDisposal.clear();
}
output:
W/Debug:: First: 0
W/Debug:: doOnDispose: observable has been disposed // I dispose Observable chain here.
W/Debug:: First: 1
W/Debug:: First: 2
W/Debug:: First: 3
W/Debug:: First: 4
As you can just see in above log output that when i dispose given rxJava observable chain only first observable stops emitting items.
I want to stop all observable those are chained.
What is the idiomatic way to solve this issue?
Two things:
flatMap may pre-consume items from upstream (up to 16 on android);
Second and more applicable to your use-case, before you call onNext you should check whether the observer is disposed (via .isDisposed()) and abort when that happens.
Also, the second flatMap gets terminated (actually it never gets called). The first one continues.
EDIT
private void testLoadData() {
Disposable disposable = Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
if(sbr.isDisposed()) return; // this will cause subscription to terminate.
Thread.sleep(3000);
Log.w("Debug: ", "First: " + i);
sbr.onNext(true);
}
sbr.onComplete();
}).subscribeOn(Schedulers.io()).flatMap(value -> Observable.create(sbr -> {
for (int i = 0; i < 5; i++) {
Thread.sleep(3000);
Log.w("Debug: ", "Second: " + i);
sbr.onNext(true);
}
sbr.onComplete();
})).doOnNext(value -> {
Log.w("Debug: ", "doONNext");
}).doOnDispose(()-> {
Log.w("Debug: ", "doOnDispose: observable has been disposed");
}).subscribe();
testCompositeDisposal.add(disposable);
}

How to sort using RxAndroid

How to sort my list through Rx function, My list contain three type of different source then I want to display my list sorted using date, how to apply that using RxAndroid?
subscriptions.add(complaintsAPI.getComplaintsAPI(userDetails.getUsername())
.compose(ReactiveUtils.applySchedulers())
.map(list -> {
List<ComplaintsRowModel> rowModel = new ArrayList<>();
for (Complaint complaint : list.getComplaints()) {
rowModel.add(new ComplaintsRowModel(complaint.getDisputeNo(),
complaint.getOpenDate(), complaint.getArea(), complaint.getStatus()));
model.complaintsList.put(complaint.getDisputeNo(), complaint);
}
for (OnlineRequest onlineRequest : list.getOnlineRequests()) {
rowModel.add(new ComplaintsRowModel(onlineRequest.getRequestNo(), onlineRequest.getOpenDate(),
onlineRequest.getArea(), onlineRequest.getStatus()));
model.complaintsList.put(onlineRequest.getRequestNo(), onlineRequest);
}
for (LlTickets llTickets : list.getLlTickets()) {
rowModel.add(new ComplaintsRowModel(llTickets.getTicketNo(), llTickets.getOpenDate(),
llTickets.getType(), llTickets.getStatus()));
model.complaintsList.put(llTickets.getTicketNo(), llTickets);
}
return rowModel;}
).toSortedList(){
//how to sort here
}).subscribe(new RequestSubscriber<List<ComplaintsRowModel>>(view.getContext(), view.progressView) {
#Override
public void onFailure(RequestException requestException) {
view.showError(requestException);
}
#Override
public void onNoData() {
super.onNoData();
isAllDataLoaded = true;
view.noDataFound();
model.setNoDataFound(true);
}
#Override
public void onNext(List<ComplaintsRowModel> complaintsRowModels) {
isAllDataLoaded = true;
model.setRowModel(complaintsRowModels);
view.buildList(complaintsRowModels);
}
}));
I think in toSortedList() can I sort my list but I don't know the way to apply that.
The toSortedList operator would only work on Observable<ComplaintRowModel> while what you have is Observable<List<ComplaintRowModel>>. So first you have to transform your observable with
flatMapIterable(complaintRowModels -> complaintRowModels)
to map it to an observable of the list elements. Then you can apply the sorting something like
toSortedList((complaintRowModel, complaintRowModel2) -> {
Date date = complaintRowModel.getDate();
Date date2 = complaintRowModel2.getDate();
// comparing dates is very much dependent on your implementation
if (date <before> date2) {
return -1;
} else if (date <equal> date2) {
return 0;
} else {
return 1;
}
})
Then you get an observable of sorted list.
As per you don't want to provide specific information about your problem, there is generic answer.
When data object which need to be sorted implements Comparable or it's primitive type.
Observable.just(3, 2, 1)
.toSortedList()
.subscribe(list -> System.out.print(Arrays.toString(list.toArray())));
[1, 2, 3]
When data object which need to be sorted doesn't implement Comparable or implements it, but you need to specify how you'd like to sort data.
That sample illustrate how to sort list of objects by val field in descended order.
static class ToSort {
Integer val;
public ToSort(Integer val) {
this.val = val;
}
#Override
public String toString() {
return "ToSort{" +
"val=" + val +
'}';
}
}
public static void main(String[] args) {
Observable.just(new ToSort(1), new ToSort(2), new ToSort(3))
.toSortedList((o1, o2) -> (-1) * o1.val.compareTo(o2.val))
.subscribe(list -> System.out.print(Arrays.toString(list.toArray())));
}
[ToSort{val=3}, ToSort{val=2}, ToSort{val=1}]
Different approach to replce the
// comparing dates is very much dependent on your implementation
if (date <before> date2) {
return -1;
} else if (date <equal> date2) {
return 0;
} else {
return 1;
}
Using a static import java.util.Comparator.comparing;
you can do comparing using method references found inside of your date object.
Ex.
In this instance unsortedDevices is a standard ObservableList to be used in a TablView.
SortedList<HistoryDisplayItem> sortedItems = new SortedList<>(unsortedDevices,
comparingInt(HistoryDisplayItem::getNumber));

Retrofit + RxJava chain multiple requests and aggregate the result

I am looking for the way how to chain multiple but same API requests with different parameters. So far my method looks like this:
#Override
public Observable<List<Entity>> getResult(Integer from, Integer to, Integer limit) {
MyService myService = restClient.getMyService();
if (null != from && null != to) {
Observable<List<Response>> responseObservable = myService.get(from, limit);
for (int i = from + 1; i <= to; i++) {
responseObservable = Observable.concat(responseObservable, myService.get(i, limit));
}
return responseObservable.map(mapResponseToEntity);
} else {
int fromParameter = null == from ? DEFAULT_FROM : from;
return myService.get(fromParameter, limit).map(mapResponseToEntity);
}
}
I expected that concat method combines Oservables data into one stream and returns combined Observable but I am getting only the last one calls result. However, in logcat I can see that correct number of calls to API was made.
Try using Observable.merge() and Observable.toList() as follows:
List<Observable<Response>> observables = new ArrayList();
// add observables to the list here...
Subscription subscription = Observable.merge(observables)
.toList()
.single()
.subscribe(...); // subscribe to List<Response>

Android - Create json object with asynctask

I've the following code :
(basically it creates various JSONObjects (beneficiario) and put them all inside another JSONObject (proposta). I don't show it here but I've Cursors (ppt, c) created before
if (ppt.getString(45).equals("0")) {
int i = 0;
c.moveToFirst();
JSONObject ben = new JSONObject();
try {
while (c.isAfterLast() == false)
{
i++;
ben.put("b_nome" + i, c.getString(1));
ben.put("b_telefone" + i, c.getString(2));
ben.put("b_nif" + i, c.getString(3));
ben.put("b_bi" + i, c.getString(4));
ben.put("b_codigopostal" + i, c.getString(5));
ben.put("b_localidade" + i, c.getString(6));
ben.put("b_morada" + i, c.getString(7));
}
} catch (JSONException e) {
e.printStackTrace();
}
proposta.put("beneficiario" + i, ben);
}
And it gives a outofmemory error, I guess that's because I'm running it on main thread.
Can you give me an help/some code to use a thread or asynctask to do it?
use c.moveToNext(); in your While loop. Your Cursor is running Infinite.
Use async task something like this :
// You must provide types for the three generic parameters before the code will compile.
// For more details, see http://developer.android.com/reference/android/os/AsyncTask.html
private class MoveOutOfUIthread extends AsyncTask<
Params, // one or more values of this type are passed to doInBackground()
Progress, // the type of the progress units published during background crunching.
Result // the type of the result returned by doInBackground()
>
{
protected Integer doInBackground(Params... p1, p2, p3) {
// your background task here
Result result = new Result();
return result;
}
protected void onPostExecute(Result r) {
// this gets the object returned by doInBackground, and executes on the UI thread
}
}
*Execute async task like this : *
new MoveOutOfUIthread().execute(p1, p2, p3);
its not intercessory to pass parameters you can simply do this :
new MoveOutOfUIthread().execute();
NOTE : You cannot change UI elements in doInBackground. You have to put the code in onPreExecute or onPostExecute which change the UI.
UPDATE :
This is to run your code in background thread. And as per your OutOfMemory error I think you should use c.moveToNext(); in your while loop as M Mohsin Naeem answered.

Categories

Resources