How to emit multiple value in one Observable - android

I am trying to download file with RxAndroid and Retrofit, I want my Observable can emit download status on processing.
My expectation:
Observable.create()
.emit(PENDING)
.emit(START)
.flatMap(() -> {
emit(DOWNLOADING);
return apiService.download();
})
.onSuccess(() -> {
emit(SUCCESS);
)
.onError(() -> {
emit(ERROR);
})

You can check the download status with the desired time interval and update the status:
getDownloadStatus()
.zipWith(Observable.interval(500, TimeUnit.MILLISECONDS), (status, interval) -> (String) status)
.repeat()
.distinctUntilChanged()
.doOnNext(status -> ...). //some other operators

Use
Observable.create (emitter -> {})
By emitter.emit(value)
you can do it. But remember free the observable when you've done the task

Related

Sending data over the air and after completion end the operation using RxAndroidBLE

I am trying to send the 128 bytes of the block to BLE Controller using the RxAndroidBle library. the flow to send data from mobile to BLE controller is as follows
Connect with BLE Controller
Start OTA (sending 1)
Send CRC (of the data block)
Send data block
wait for 2 seconds
repeat step 3
END OTA (sending 2)
Here is snapshot of a code
.flatMap(rxBleConnection -> prepareWriting())
.flatMapIterable(otaMetaData -> otaMetaData)
.zipWith(Observable.interval(2, TimeUnit.SECONDS), (item, interval) -> item)
.doOnNext(metaData -> {
otaMetaData = metaData;
})
.map(otaMetaData -> {
return mRxBleConnection.writeCharacteristic(OTA_CHECKSUM, otaMetaData.getCrcBlock()).toObservable();
})
.doOnNext(otaMetaData -> {
Log.e(TAG, "Writing CRC " + Arrays.toString(BLEUtils.toHex(otaMetaData.getCrcBlock())));
})
.map(bytes -> {
return mRxBleConnection.writeCharacteristic(OTA_DATA, otaMetaData.getDataBlock()).toObservable();
})
.doOnNext(otaMetaData -> {
Log.e(TAG, "Writing Data " + Arrays.toString(BLEUtils.toHex(otaMetaData.getDataBlock())));
})
.flatMap(bytes -> mRxBleConnection.writeCharacteristic(OTA_CONTROL,OTA_DATA_END).toObservable())
The problem is while sending the END OTA because as the flatMapIterable returns 20 items, .flatMap(bytes -> mRxBleConnection.writeCharacteristic(OTA_CONTROL,OTA_DATA_END) is getting called 20 times.
So, I am not sure how I can send the OTA_DATA_END command when all the 20 items get processed. Moreover, any suggestion to improve the existing code is welcome.
You can use flatMapIterable() with toList(). Try to add toList() operator before OTA_DATA_END command like:
.toList() // wait for all commands to complete
.flatMap(bytes -> mRxBleConnection.writeCharacteristic(OTA_CONTROL,OTA_DATA_END).toObservable())
EDIT
Better to separate steps like
.flatMap(rxBleConnection -> prepareWriting())
.flatMap(otaMetaData -> Observable.fromIterable(otaMetaData)
.zipWith(Observable.interval(2, TimeUnit.SECONDS), (metaData, interval) -> metaData)
.flatMap(metaData -> {
return mRxBleConnection.writeCharacteristic(OTA_CHECKSUM, metaData.getCrcBlock())
.toObservable();
}, (metaData, bytes) -> metaData) /* result selector */
.flatMap(metaData -> {
return mRxBleConnection.writeCharacteristic(OTA_DATA, metaData.getDataBlock())
.toObservable();
}, (metaData, bytes) -> metaData)
.toList()
.toObservable()
)
.flatMap(otaMetaData -> {
return mRxBleConnection.writeCharacteristic(OTA_CONTROL, OTA_DATA_END)
.toObservable();
})

RxJava if/else with multiple chain calls

I have a webservice call that return an object in which there is a parameter that indicates whether the operation ended successfully or not, so I would like to filter it (kind of if/else statement) inside the RxJava chain by using RxJava operators. Is it possible?
Something like this but not using if/else:
repo.webserviceCall(username, password)
.flatMap(result -> {
if (result.isSuccessful())
repo.secondWebserviceCall(result.getInfo())
else
showToastMessage("Api call not successful"); //STOP FLOW HERE
})
.flatMap(result -> thirdWebserviceCall(res))
.subscribe(res -> {showSuccessMssg(res)}, throwable -> { showError(t)});
You can return an error() from your flatMap so that the execution then goes to the onError consumer in your subscribe call.
If each service call returns one item, you could rearrange the operators so that not successful won't run the flatMap for the second and third calls. The filter will turn the setup to empty for which you can use the onComplete handler to display the toast.
repo.webserviceCall(username, password)
.filter(result -> result.isSuccessful())
.flatMap(result ->
repo.secondWebserviceCall(result.getInfo())
.flatMap(result -> thirdWebserviceCall(res))
)
.subscribe(
res -> showSuccessMssg(res),
throwable -> showError(t),
() -> showToastMessage("Api call not successful")
);

RxJava 2 Observable with flatMapCompletable doesn't complete

I have an observable that emits items and upload them to server.
Here is the code:
repository
.getItems()
.doOnComplete(() -> Log.d(TAG, "No items left."))
.flatMapCompletable(item ->
repository
.uploadItems(item)
.onErrorComplete()
.andThen(
deleteTemporaryItem()
.onErrorComplete()
)
);
getItems method emits items one by one and then completes, uploadItems method upload them to server. The issue is when there is no items all chain onComplete event just working fine and all my subscribers get this event and proceed it BUT when there were some items and all of them were uploaded onComplete events doesn't go further than .doOnComplete(() -> Log.d(TAG, "No items left.")) method and all subscribers doesn't get this event. I added onErrorComplete to be sure that all methods after uploadItems completes and I also see in logs that all of them were completed but onComplete event from repository.getItems() doesn't go to all subscribers.
Could anyone please help to figure out what could be the reason for this behavior?
Thanks in advance!
Please have a look at this example:
I pass the item through each step, so the subscribe will be notified on each item that has been processed. The processing pipeline involves uploading and deleting the file.
Please try to change the implementation and post a log of the output.
#Test
void name() throws Exception {
Flowable<Integer> completed_work = Flowable.just(1, 2, 3)
.map(integer -> integer * 1000)
.flatMapSingle(integer ->
Completable.fromAction(() -> {
Thread.sleep(integer);
// do upload stuff here
})
.doOnComplete(() -> System.out.println("Uploaded file ...."))
//.timeout(10, TimeUnit.SECONDS)
.retry(3)
.andThen(
Completable.fromAction(() -> {
// do delete stuff...
})
.retry(2)
//.timeout(10, TimeUnit.SECONDS)
.doOnComplete(() -> System.out.println("Deleted file ..."))
)
.toSingle(() -> integer)
)
.doOnComplete(() -> System.out.println("Completed work"));
completed_work.test()
.await()
.assertResult(1000, 2000, 3000);
}

RxAndroid Observable running on unexpected thread

I'm trying to create an Observable such that it will load some data from the network on an interval, and whenever the user refreshes the page. This is the gist of what I have so far:
PublishSubject<Long> refreshSubject = PublishSubject.create();
Observable<MyDataType> observable = Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.flatMap(t -> {
// network operations that eventually return a value
// these operations are not observables themselves
// they are fully blocking network operations
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
// update ui with data
}, error -> {
// do something with error
});
Later in a refresh callback I have:
refreshSubject.onNext(0L);
It runs on the interval fine, however when I refresh, it explodes with a NetworkOnMainThreadException. I thought that I handled this with subscribeOn/observeOn. What am I missing? Also, why doesn't this cause a crash when the Observer is triggered from the interval?
You have to change your subscribeOn(Schedulers.io()) to observeOn(Schedulers.io()) and move it over your flatMap.
The reason for this is that your refreshSubject is a PublishSubject, which is an Observable and an Observer.
Since the onNext() of this PublishSubject is called inside the intern Observable first before the result gets delivered to your subscription.
This is also the reason that it works when you just use your Observable(and the fact that interval always subscribes to the computation thread by default).
Just check the output of those two snippets:
Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.observeOn(Schedulers.io())
.doOnNext(aLong -> Log.d("Thread", Thread.currentThread().toString()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
Log.d("Subscribe Thread", Thread.currentThread().toString());
}, error -> {
// do something with error
});
vs
Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.doOnNext(aLong -> Log.d("Thread", Thread.currentThread().toString()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
Log.d("Subscribe Thread", Thread.currentThread().toString());
}, error -> {
// do something with error
});

RxJava infinite stream best practice

In android app i have this case:
Listen to my editText with observable:
WidgetObservable.text(myEditText, false)
.map { it.text().toString() }
.debounce(800, TimeUnit.MILLISECONDS, Schedulers.io())
Then i need to send network request with string emitted by observable:
.flatMap { networkObservable.subscribeOn(Schedulers.io()) }
My question is: what is the best possible way to write infinite stream of these network results.
Errors handled by UI.
Unsubscription done with AppObservable.bindActivity() wrapper
I ended up attaching materialize() operator to network observable, and then handling it like:
.subscribe{
when (it.getKind()) {
Kind.OnNext -> text.setText(it.getValue())
Kind.OnError -> text.setText(it.getThrowable().getMessage())
}
}
Do you know better way, or its just fine?
At least it works.
P.S. another useful case will be Refresh button clicks flatMap'ed to network calls
You can use onErrorResumeNext to recovery your Observable from a failure. E.g.,
WidgetObservable.text(myEditText, false)
.map { it.text().toString() }
.debounce(800, TimeUnit.MILLISECONDS, Schedulers.io())
.flatMap {
networkObservable.subscribeOn(Schedulers.io())
.onErrorResumeNext(t -> t.getMessage())
}

Categories

Resources